Skip to content

Instantly share code, notes, and snippets.

@Jaredk3nt
Last active November 9, 2024 13:08
Show Gist options
  • Save Jaredk3nt/bee978b6e1b73541e864aa7c365bea05 to your computer and use it in GitHub Desktop.
Save Jaredk3nt/bee978b6e1b73541e864aa7c365bea05 to your computer and use it in GitHub Desktop.
React Context Thunk Pattern
const MyContext = createContext();
function reducer(state, action) {
switch (action.type) {
case "COUNT_REQUEST":
return {
...state,
loading: true,
err: undefined
};
case "COUNT_RECIEVE":
return {
...state,
loading: false,
count: action.response
};
case "COUNT_ERROR":
return {
...state,
loading: false,
err: action.err
};
}
}
function useThunk(reducer, initialState) {
// Keep track of mounted state to prevent async calls for updating state and leaking
const mounted = useRef(true);
useEffect(() => () => (mounted.current = false), []);
// Setup underlying useReducer core
const [state, dispatch] = useReducer(reducer, initialState);
// Create a "safe" dispatch for using with async ops
function mountedDispatch(action) {
if (mounted.current) {
dispatch(action);
}
}
// Create thunk function for request dispatch chains
function thunk(options, actions, args = {}, callback) {
mountedDispatch({ type: actions[0], args });
request(options)
.then(response => {
mountedDispatch({ type: actions[1], response, args });
if (callback) callback();
})
.catch(error => {
mountedDispatch({ type: actions[2], error, args });
});
}
// Return in 'tuple' [state, thunk, dispatch]
return [state, thunk, mountedDispatch];
}
function MyProvider(props) {
const thunk = useThunk(reducer, { count: 0, loading: false, err: undefined });
return <MyContext.Provider value={thunk} {...props} />;
}
function useThunkContext() {
const context = useContext(MyContext);
if (!context) {
throw new Error("Must be used within <MyProvider />");
}
return context;
}
function RequestComponent() {
const [state, thunk, dispatch] = useThunkContext();
function getCount() {
thunk({ url }, ["COUNT_REQUEST", "COUNT_RECIEVE", "COUNT_ERROR"]);
}
if (state.err) {
return <p>{state.err}</p>;
}
if (state.loading) {
return <p>Loading...</p>;
}
return <p>{state.count}</p>;
}
function App() {
return (
<MyProvider>
<RequestComponent />
</MyProvider>
);
}
@builtbyproxy
Copy link

Beauty thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment