Skip to content

Instantly share code, notes, and snippets.

@sophiebits
Forked from davidkpiano/reducer-with-effects.js
Last active July 29, 2020 12:10
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sophiebits/be37b65e34ec0d5f90bdd29a3d5e3bbf to your computer and use it in GitHub Desktop.
Save sophiebits/be37b65e34ec0d5f90bdd29a3d5e3bbf to your computer and use it in GitHub Desktop.
An idea for actor-model-based effects with reducers
import {
useReducerWithEffects,
emitEffect,
} from 'react';
// In a component
const UserComponent = () => {
function fetchUserEffect(id, parentRef) {
const controller = new AbortController();
return {
effect() {
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 })
);
},
send(event) {
if (event.type === 'CANCEL') {
controller.abort();
parentRef.send({ type: 'CANCELLED' });
}
}
}
}
function fetchUserReducer(state, event) {
const ownRef = { send: dispatch };
if (event.type === 'FETCH') {
const fetchRef = fetchUserEffect(userId, ownRef);
emitEffect(fetchRef.effect);
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'
};
// 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