Skip to content

Instantly share code, notes, and snippets.

@elsangedy
Created September 5, 2019 16:54
Show Gist options
  • Save elsangedy/ef6c93de32a864cacbd35aaa29a213c6 to your computer and use it in GitHub Desktop.
Save elsangedy/ef6c93de32a864cacbd35aaa29a213c6 to your computer and use it in GitHub Desktop.
useCombineReducer
import { useMemo, useCallback, useReducer as useReducerBase } from "react";
export function useCombineReducer(initialState, ...reducers) {
const internalReducer = useCallback(
(state, action) => ({
...state,
...reducers.reduce(
(prev, current, idx) =>
idx === 0
? current(state, action, initialState)
: current(state, prev, action, initialState),
{}
)
}),
[reducers, initialState]
);
const [state, dispatch] = useReducerBase(internalReducer, initialState);
const customDispatch = useCallback(
(type, payload) => dispatch({ type, payload }),
[dispatch]
);
const values = useMemo(() => [state, customDispatch, dispatch], [
state,
customDispatch,
dispatch
]);
return values;
}
import { useMemo, useEffect } from "react";
import { useFetchMachine } from "./useFetchMachine";
const delay = (ms = 300) => new Promise(resolve => setTimeout(resolve, ms));
export function useFetch() {
function stateReducer(state, stateChanges, { type, payload }) {
switch (state.current) {
case "PENDING":
switch (type) {
case "RESOLVE":
stateChanges.data = payload;
break;
case "REJECT":
if (state.retries >= 3) {
stateChanges.current = "ERROR";
}
break;
}
break;
case "FAILURE":
switch (type) {
case "RETRY":
stateChanges.current = "PENDING";
stateChanges.retries = state.retries + 1;
break;
}
break;
}
return stateChanges;
}
const fetchMachine = useFetchMachine({
stateReducer,
initialState: {
data: [],
retries: 0
}
});
const isPending = useMemo(() => fetchMachine.state.current === "PENDING", [
fetchMachine.state.current
]);
const fetch = async () => {
await delay(1000);
fetchMachine.resolve([1, 2, 3, 4]);
};
useEffect(() => {
if (isPending) {
fetch();
}
}, [isPending]);
const retry = payload => fetchMachine.dispatch("RETRY", payload);
return {
...fetchMachine,
retry
};
}
import { useCombineReducer } from "./useCombineReducer";
function reducer(state, { type }, initialState) {
let stateChanges = {};
switch (state.current) {
case "IDLE":
switch (type) {
case "FETCH":
stateChanges.current = "PENDING";
break;
}
break;
case "PENDING":
switch (type) {
case "RESOLVE":
stateChanges.current = "SUCCESS";
break;
case "REJECT":
stateChanges.current = "FAILURE";
break;
}
break;
default:
switch (type) {
case "RESET":
stateChanges = {
...initialState
};
break;
}
}
return stateChanges;
}
export const useFetchMachine = ({
stateReducer = (_, newState) => newState,
initialState = {}
} = {}) => {
const [state, dispatch] = useCombineReducer(
{
current: "IDLE",
...initialState
},
reducer,
stateReducer
);
const fetch = payload => dispatch("FETCH", payload);
const resolve = payload => dispatch("RESOLVE", payload);
const reject = payload => dispatch("REJECT", payload);
const reset = payload => dispatch("RESET", payload);
return {
state,
fetch,
resolve,
reject,
reset,
dispatch
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment