如何将 useReducer 与 useQuery 结合使用?

时间:2021-03-01 01:01:27

标签: reactjs use-reducer react-query

import api from "api";
import { Loader } from "components/base";
import { useReducer } from "react";
import { useQuery } from "react-query";

function reducer(state, { type, payload }) {
  switch (type) {
    case "init":
      return state.concat(payload);
    default:
      throw new Error();
  }
}

function Table() {
  const [workers, dispatch] = useReducer(reducer, []);

  const fetchWorkers = async () => {
    const workersData = await api.index();
    return workersData;
  };

  const { status, data, error } = useQuery("", fetchWorkers);

  switch (status) {
    case "loading":
      return <Loader />;

    case "error":
      return <p className="text-red-600">{error.message}</p>;

    case "success":
      dispatch({ type: "init", payload: data });
      return <p>Success!</p>;

    default:
      return <p>?</p>;
  }
}

export default Table;

上面的代码导致无限重新渲染?为什么? ?

一旦 useQuery 传达了 status === "success",为什么我不能只 dispatch 并初始化我的数据?我应该怎么做呢? ?

我删除了 useQuery 并使用了 useEffect 没有任何问题 - 数据回来了,我 dispatch ed。

这里与 useQuery 有何不同?

1 个答案:

答案 0 :(得分:3)

您的代码导致无限重新渲染,因为您在数据加载后每次渲染都调用 dispatch

case "success":
  // since the switch() statement runs every render, as long as it's "success"
  // we'll call dispatch, update the reducer, and then force a re-render
  // thus causing a loop
  dispatch({ type: "init", payload: data });
  return <p>Success!</p>;

有两种方法可以避免这种情况。

  1. 不要将数据放入您的减速器中。看起来您没有任何理由在示例代码中这样做,如果在更多地方需要数据,您也可以在那里调用 useQuery(我不太熟悉图书馆,但我想他们有一些缓存策略)。

  2. 如果你真的需要reducer中的数据,请在效果中进行分派,这样它只会在状态更改为success时运行

    React.useEffect(() => {
      if (status === 'success') {
        dispatch({ type: 'init', payload: data });
      },
    }, [status]); // since `status` is in the dependency, this effect only runs when its value changes
    

一般经验是尽量避免在组件的渲染体中直接调用状态改变函数——首选效果和回调。