Skip to main content

🌐 Fetch Machine Example

In this example, you'll learn how to model loading states for an async API call using Retomus.
We'll use fetchData to go through the idle → loading → success/error flow.


Status: idle


🔧 Machine Definition

import { createRetomus, createMachineConfig } from 'retomus';

const retomus = createRetomus();

const fetchMachine = retomus.createMachine(
createMachineConfig({
id: 'fetchMachine',
status: ['idle', 'success', 'error'],
actions: ['fetch', 'reset'],
transitions: {
idle: { fetch: ['success', 'error'] },
success: { reset: 'idle' },
error: { reset: 'idle' },
},
router: {
fetch: ctx => (ctx.state.data ? 'success' : 'error'),
},
actionHandlers: {
fetch: async ({ ctx, done }) => {
try {
const res = await fetch(
'https://jsonplaceholder.typicode.com/todos/1',
);
ctx.state.data = await res.json();
} catch (err) {
ctx.state.error = err.message;
}
done();
},
reset: ({ ctx, done }) => {
ctx.state.data = null;
ctx.state.error = null;
done();
},
},
initialStatus: { status: 'idle' },
ctx: {
state: {
data: null,
error: null,
},
},
}),
);

🧩 React Component

import React, { CSSProperties } from 'react';

const FetcherComponent = ({ fetchMachine }) => {
const status = fetchMachine.useStatus();
const data = fetchMachine.useState('data');
const error = fetchMachine.useState('error');
const fetch = fetchMachine.useAction('fetch');
const reset = fetchMachine.useAction('reset');

const [loading, setLoading] = useState(false);
const handleClickFetch = async () => {
if (!loading) {
setLoading(true);
await fetch();
setLoading(false);
}
};

return (
<div style={styles.container}>
<h2>Status: {status}</h2>

{status === 'success' && (
<pre>Fetched data: {JSON.stringify(data, null, 2)}</pre>
)}
{status === 'error' && <p style={{ color: 'red' }}>{error}</p>}
{loading && <p>Loading...</p>}

<div style={styles.buttonGroup}>
<button onClick={handleClickFetch} disabled={loading}>
Fetch
</button>
<button onClick={() => reset()}>Reset</button>
</div>
</div>
);
};

const styles: {
container: CSSProperties;
buttonGroup: CSSProperties;
} = {
container: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '1rem',
},
buttonGroup: {
display: 'flex',
justifyContent: 'center',
gap: '1rem',
},
};


🧠 What You Learn

✅ Handling async side effects
✅ Branching based on try/catch
✅ Using status to render different UI states