Skip to content

Instantly share code, notes, and snippets.

@hew
Forked from davidkpiano/reducer-with-effects.js
Created August 20, 2021 18:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hew/2d12034bb92986fbd961977f6c0b773e to your computer and use it in GitHub Desktop.
Save hew/2d12034bb92986fbd961977f6c0b773e to your computer and use it in GitHub Desktop.
An idea for actor-model-based effects with reducers
import {
useReducerWithEffects,
// hand-wavey, but meant to be used by React to create something
// external will produce async values sent to components
createEffect
} from 'react';
function fetchUserEffect(id, parentRef) {
const controller = new AbortController();
const signal = controller.signal;
const promise = fetch(`https://example.com/api/users/${id}`, { signal });
promise.then(
data => parentRef.send({ type: 'RESOLVE', data }),
error => parentRef.send({ type: 'REJECT', error })
);
return {
send(event) {
if (event.type === 'CANCEL') {
controller.abort();
parentRef.send({ type: 'CANCELLED' });
}
}
}
}
function fetchUserReducer(state, event, ownRef) {
if (event.type === 'FETCH') {
const fetchRef = createEffect(fetchUserEffect(ownRef));
return { ...state, status: 'fetching', fetchRef };
} else if (event.type === 'CANCEL') {
// tell fetchRef to cancel
state.fetchRef.send({ type: 'CANCEL' });
} else if (event.type === 'CANCELLED') {
return {
...state,
status: 'cancelled'
};
} else if (event.type === 'RESOLVE') {
return { ...state, user: event.data, status: 'resolved' };
} else {
return state;
}
}
const initialState = {
user: null,
error: null,
status: 'idle'
};
// In a component
const UserComponent = () => {
// Same syntax as useReducer()
const [state, dispatch] = useReducerWithEffects(fetchUserReducer, initialState);
// ...
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment