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
有何不同?
答案 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>;
有两种方法可以避免这种情况。
不要将数据放入您的减速器中。看起来您没有任何理由在示例代码中这样做,如果在更多地方需要数据,您也可以在那里调用 useQuery
(我不太熟悉图书馆,但我想他们有一些缓存策略)。
如果你真的需要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
一般经验是尽量避免在组件的渲染体中直接调用状态改变函数——首选效果和回调。