diff --git a/snippets/useAsync.md b/snippets/useAsync.md new file mode 100644 index 000000000..9881094f3 --- /dev/null +++ b/snippets/useAsync.md @@ -0,0 +1,65 @@ +--- +title: useAsync +tags: hooks,state,reducer,advanced +--- + +A hook that handles asynchronous calls. + +- Create a custom hook that takes a handler function, `fn`. +- Define a reducer function and an initial state for the custom hook's state. +- Use the `React.useReducer()` hook to initialize the `state` variable and the `dispatch` function. +- Define a `run` function that will run the provided callback, `fn`, while using `dispatch` to update `state` as necessary. +- Return an object containting the the properties of `state` (`value`, `error` and `loading`) and the `run` function. + +```jsx +const useAsync = (fn) => { + const initialState = { loading: false, error: null, value: null }; + const stateReducer = (_, action) => { + switch (action.type) { + case 'start': + return { loading: true, error: null, value: null}; + case 'finish': + return { loading: false, error: null, value: action.value}; + case 'error': + return { loading: false, error: action.error, value: null}; + } + } + + const [state, dispatch] = React.useReducer(stateReducer, initialState); + + const run = async (args = null) => { + try { + dispatch({ type: 'start' }); + const value = await fn(args); + dispatch({ type: 'finish', value }); + } catch (error) { + dispatch({ type: 'error', error }); + } + }; + + return { ...state, run }; +}; +``` + +```jsx +const RandomImage = props => { + const imgFetch = useAsync(url => fetch(url).then(response => response.json())); + + return ( +