由于不更新状态,具有调度函数的 useSelector 和 UseEffect 创建循环

useSelector and UseEffect with dispatch functions create loop due to not updating state

我正在尝试开发一个使用 Hooks 显示项目列表的 React 应用程序,但发生了无限循环。我认为状态没有被识别为已更新,但我不知道为什么。

我在文档中看到它可能是我应该在 useEffect 函数上添加的依赖项,但是当我删除对 useEffect 的依赖项时,它解决了问题。 此外这不是文档推荐的做法,他们建议将所有依赖项放在列表中。

codesandbox 在这里:https://codesandbox.io/s/react-listing-forked-6sd99如果我在 Dashboard.ts 取消第 30 行的注释,可能会看到无限循环,这是调用 dispatch 函数的行(并且 codesandbox 将冻结几秒钟)。

useSelector和useEffect函数如下,位于Dashboard.tsx文件中:

  const catalog = useSelector<RootState, MetricState>(
    (state) => state.fetchMetrics
  );
  console.log(catalog);

  const dispatch = useDispatch();

  const classes = useStyles();
  useEffect(() => {
    // dispatch(appReducer.actionCreators.fetchMetrics());
  },[catalog, dispatch]));

模拟数据如下,在apiCreators.js文件

export function fetchMetrics() {
  const action = {
    type: ACTION_TYPES.FETCH_METRICS,
    payload: []
  };
  return fetchMetricsCall(action);

const fetchMetricsCall = (action) => async (dispatch) => {
  try {
    dispatch({
      type: action.type,
      payload: {
        metrics: [
          {
            name: "one",
            owner: {
              team: "test"
            }
          },
          {
            name: "one",
            owner: {
              team: "test"
            }
          }
        ] //contains the data to be passed to reducer
      }.metrics
    });
  } catch (e) {
    dispatch({
      type: ACTION_TYPES.FAILURE,
      payload: console.log(e) //TODO: fix return type
    });
  }
};

主要问题是 [catalog, dispatch] 里面的 catalog 在这里找到:

useEffect(() => {
    // dispatch(appReducer.actionCreators.fetchMetrics());
  },[catalog, dispatch]));

问题是 dispatch(appReducer.actionCreators.fetchMetrics()) 导致 state.fetchMetrics (aka catalog) 更新(这是一个对象),而 javascript 在比较 objects/arrays 时是“奇怪的” .

让我们看看这个:

let x = {a: 0}
let y = {a: 0}
let z = x

console.log(x === y) // this is FALSE
console.log(x === x) // this is TRUE
console.log(z === x) // this is TRUE

本文更深入地介绍了它:http://adripofjavascript.com/blog/drips/object-equality-in-javascript.html

useEffect 会在状态发生变化时执行类似 if(oldState.catalog !== newState.catalog) {...rerender component} 的操作(我觉得这可能真的不准确,但我希望它能帮助说明发生了什么)。因此,组件重新呈现并重新调用 dispatch,这会更新状态,并且由于 useEffect 中的一个依赖项是一个对象,因此不可能进行更新来告诉 useEffect “停止重新渲染”。

有多种方法可以解决此问题,但您不需要 [catalog, dispatch] 中的 catalog。简单地删除它似乎可以解决问题?