Last active
May 10, 2022 00:01
-
-
Save dr-skot/05be1e4aebe6195e90e8efd68e23a1d1 to your computer and use it in GitHub Desktop.
React hook like useReducer but can update state asynchronously
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// useAsyncReducer works like useReducer, except the reducer has access to the dispatch function | |
// so it can change state after async operations complete | |
// example usage: | |
import useAsyncReducer from './use-async-reducer'; | |
function reducer(state, dispatch, action, args) { | |
switch (action) { | |
case 'fetch': | |
return [ | |
state, | |
fetch(args.url).then((fetched) => { dispatch('processFetch', { fetched }); return fetched }), | |
]; | |
case 'processFetch': | |
return [{ ...state, ...processFetch(args.fetched) }]; | |
default: | |
return [state]; | |
} | |
} | |
function FetchingComponent({ url, initialState }) { | |
const [state, dispatch] = useAsyncReducer(initialState); | |
useEffect(() => { | |
dispatch('fetch', url); | |
}, [url]); | |
return <AmazingDataView data={state}/>; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// useAsyncReducer works like useReducer, except the reducer has access to the dispatch function | |
// so it can change state after async operations complete | |
import { useCallback, useState } from 'react'; | |
// dispatch returns the promise returned by the reducer, if any (otherwise returns Promise.resolve()) | |
type AsyncDispatcher<Action> = (action: Action, ...args: any[]) => Promise<any>; | |
type AsyncReducer<State, Action> = ( | |
state: State, | |
redispatch: AsyncDispatcher<Action>, // redispatch to change state after async completes | |
action: Action, | |
...args: any[] // use as many arguments as you like | |
) => [State, Promise<any>?]; // if the reducer return a promise, it will passed on by dispatch() | |
function useAsyncReducer<State, Action>( | |
reducer: AsyncReducer<State, Action>, | |
initialState: State | |
) { | |
const [state, setState] = useState(initialState); | |
const dispatch: AsyncDispatcher<Action> = useCallback( | |
(action, ...args) => | |
new Promise((resolve, reject) => { | |
setState((prevState) => { | |
const [nextState, _promise] = reducer( | |
prevState, | |
dispatch, | |
action, | |
...args | |
); | |
(_promise || Promise.resolve()).then(resolve).catch(reject); | |
return nextState; | |
}); | |
}), | |
[reducer] | |
); | |
return [state, dispatch]; | |
} | |
export default useAsyncReducer; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment