React useAsync hook

处理异步调用。

  • 创建一个自定义钩子,接受一个处理函数 fn
  • 为自定义钩子的状态定义一个 reducer 函数和一个初始状态。
  • 使用 useReducer() 钩子初始化 state 变量和 dispatch 函数。
  • 定义一个异步的 run 函数,该函数将运行提供的回调函数 fn,并使用 dispatch 根据需要更新 state
  • 返回一个包含 state 属性(valueerrorloading)和 run 函数的对象。
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}
      >
        加载图片
      </button>
      <br />
      {imgFetch.loading && <div>加载中...</div>}
      {imgFetch.error && <div>错误 {imgFetch.error}</div>}
      {imgFetch.value && (
        <img
          src={imgFetch.value.message}
          alt="avatar"
          width={400}
          height="auto"
        />
      )}
    </div>
  );
};

ReactDOM.createRoot(document.getElementById('root')).render(
  <RandomImage />
);