Skip to content

Instantly share code, notes, and snippets.

@arabold
Last active April 20, 2021 21:47
Show Gist options
  • Save arabold/b6495cdd20dc7737e330d5a1793f70a8 to your computer and use it in GitHub Desktop.
Save arabold/b6495cdd20dc7737e330d5a1793f70a8 to your computer and use it in GitHub Desktop.
import { useCallback, useRef, useState } from "react";
export type Dispatch<TState> = (action: ThunkAction<TState>) => Promise<void>
/** A thunk action that can be dispatched by the {@link useThunkReducer}. */
export type ThunkAction<TState> = (
dispatch: Dispatch<TState>,
getState: () => Readonly<TState>,
) => Promise<TState | undefined | void> | TState | undefined | void;
/**
* Asynchronous action reducer inspired by the popular [Redux Thunk Middleware](https://github.com/reduxjs/redux-thunk).
*
* @param initialState
*/
export const useThunkReducer = function <TState>(initialState: TState): [TState, Dispatch<TState>] {
const [state, setState] = useState<TState>(initialState);
const stateRef = useRef<TState>(state);
const dispatch = useCallback(async (action: ThunkAction<TState>) => {
try {
const nextState = await action(dispatch, () => stateRef.current);
if (typeof nextState !== "undefined") {
stateRef.current = nextState; // Update the ref right away; synchronously
setState(nextState as TState);
}
} catch (error) {
console.error(`Dispatching action ${action.name} failed`, { error });
throw error;
}
}, []);
return [state, dispatch];
};
export default useThunkReducer;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment