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 thestate
variable and thedispatch
function. - Define a
run
function that will run the provided callback,fn
, while usingdispatch
to updatestate
as necessary. - Return an object containting the the properties of
state
(value
,error
andloading
) and therun
function.
代码实现
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 };
};
使用样例
const RandomImage = props => {
const imgFetch = useAsync(url => fetch(url).then(response => response.json()));
return (
<div>
<button
onClick={() => imgFetch.run('https://dog.ceo/api/breeds/image/random')}
disabled={ imgFetch.isLoading }
>
Load image
</button>
<br/>
{ imgFetch.loading && <div>Loading...</div> }
{ imgFetch.error && <div>Error { imgFetch.error }</div> }
{ imgFetch.value && <img src={ imgFetch.value.message } alt="avatar" width={400} height="auto" />}
</div>
);
};
ReactDOM.render(<RandomImage />, document.getElementById('root'));