Skip to content

Instantly share code, notes, and snippets.

@vhuerta
Last active May 7, 2023 01:16
Show Gist options
  • Select an option

  • Save vhuerta/bcdec174933caec250962d484ddc16b8 to your computer and use it in GitHub Desktop.

Select an option

Save vhuerta/bcdec174933caec250962d484ddc16b8 to your computer and use it in GitHub Desktop.
Add thunk capabilities to useReducer and integrates with redux dev-tools
// Actions Type Definition
type AsyncActionPending = {
type: "ASYNC_ACTION_PENDING";
};
type AsyncActionFulfilled = {
type: "ASYNC_ACTION_FULFILLED";
};
type AsyncAction = AsyncActionPending | AsyncActionFulfilled;
// State type definition
type AsyncState = {loading: boolean};
// Reducer
function reducer(
state: AsyncState,
{ type }: AsyncAction
) {
switch (type) {
case "ASYNC_ACTION_PENDING":
return {...state, loading: true};
case "ASYNC_ACTION_FULFILLED":
return {...state, loading: false};
default:
return state;
}
}
const [state, dispatch] = useThunkReducer(reducer, {loading: false}, "async_action"); // <= Will see the state under async_action prop on redux devtools
const asynctAction = () => {
return (dispatch: Dispatch<AsyncAction>, getState: () => AsyncState) => {
console.log(getState()) // <= Get the current state within the action
dispatch({ type: "ASYNC_ACTION_PENDING" }); // Async action started, now loading is true
setInterval(() => dispatch({ type: "ASYNC_ACTION_FULFILLED" }), 3e3); // Complete action after 3 seconds, now loading is false again
};
};
import { useCallback, Reducer, ReducerState, ReducerAction } from "react";
import { useReducer } from "reinspect";
export type StateGetter<A extends Reducer<any, any>> = () => ReducerState<A>;
export type DispatchThunk<A extends Reducer<any, any>> = (
dispatch: (value: ReducerAction<A>) => void,
state: StateGetter<A>
) => void;
export type DispatcherThunk<A extends Reducer<any, any>> = (
action: ReducerAction<A> | DispatchThunk<A>
) => void;
export type ActionOrThunk<A extends Reducer<any, any>> =
| ReducerAction<A>
| DispatchThunk<A>;
function isDispatchThunk<R extends Reducer<any, any>>(
action: ReducerAction<R> | DispatchThunk<R>
): action is DispatchThunk<R> {
return typeof action === "function";
}
/**
* Augments React's useReducer() hook so that the action
* dispatcher supports thunks.
*/
export function useThunkReducer<R extends Reducer<any, any>>(
reducer: R,
initialState: ReducerState<R>,
id: string
): [ReducerState<R>, DispatcherThunk<R>] {
const [state, dispatch] = useReducer(
reducer,
initialState,
state => state,
id
);
const getState = useCallback(() => state, [state]);
const dispatchThunk = (action: ActionOrThunk<R>): void => {
return isDispatchThunk(action)
? action(dispatch, getState)
: dispatch(action);
};
return [state, dispatchThunk];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment