Skip to content

Instantly share code, notes, and snippets.

@christianchown
Last active May 24, 2020 19:18
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 christianchown/d56a582c7795a1524b385e0f6be023cb to your computer and use it in GitHub Desktop.
Save christianchown/d56a582c7795a1524b385e0f6be023cb to your computer and use it in GitHub Desktop.
Convert any useReducer() dispatch to be able to allow its actions to be passed to Redux-like middleware
import React from 'react';
export type ReducerAction = Record<string, any> & {
type: string;
};
type Middleware = (a: ReducerAction) => void | Promise<void>;
type Middlewares = Middleware[];
const initialState: Middlewares = [];
const MiddlewareState = React.createContext<undefined | Middlewares>(undefined);
export const MiddlewareSetState = React.createContext<
undefined | React.Dispatch<React.SetStateAction<Middlewares>>
>(undefined);
export function MiddlewareContainer({children}: {children: React.ReactNode}) {
const [middlewares, setMiddlewares] = React.useState(initialState);
return (
<MiddlewareState.Provider value={middlewares}>
<MiddlewareSetState.Provider value={setMiddlewares}>
{children}
</MiddlewareSetState.Provider>
</MiddlewareState.Provider>
);
}
const transform = (scope: string) => (action: ReducerAction) => ({
...action,
type: `${scope}/${action.type}`,
});
export function useMiddlewareDispatch<A extends ReducerAction>(
scope: string,
dispatch: React.Dispatch<A>,
): React.Dispatch<A> {
const middlewares = React.useContext(MiddlewareState);
if (middlewares === undefined) {
throw new Error(
`useMiddlewareDispatch must be used within a <MiddlewareContainer />`,
);
}
const transformAction = React.useMemo(() => transform(scope), [scope]);
return React.useCallback(
(action: A) => {
const transformedAction = transformAction(action);
middlewares.forEach((middleware) => {
middleware(transformedAction);
});
dispatch(action);
},
[transformAction, middlewares, dispatch],
);
}
export function useAddMiddleware(middleware: Middleware) {
const setMiddlewares = React.useContext(MiddlewareSetState);
if (setMiddlewares === undefined) {
throw new Error(
`useAddMiddleware must be used within a <MiddlewareContainer />`,
);
}
setMiddlewares((m) => [...m, middleware]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment