现在我不明白为什么用useSelector得到的数据值要用useEffect来监控,而用useState来呈现一个闭环的页面.

以下是我的代码:

const data = useSelector((state: DeviceList) => {
  if (state.basicSetting !== null) {
    return state.basicSetting.filter(
      item =>
        item.deviceName.includes('ac') &&
        !item.deviceName.includes('bz'),
    );
  } else {
    return null;
  }
});

const [list, setList] = useState<DeviceDataOne[][]>([]);

useEffect(() => {
  if (data !== null) {
    let arr = [];
    for (var i = 0; i < (data as DeviceDataOne[]).length; i += 4) {
      arr.push((data as DeviceDataOne[]).slice(i, i + 4));
    }
    setList(JSON.parse(JSON.stringify(arr)));
  } else {
    setList([]);
  }
}, [data]);

useEffect(() => {
  console.log('list', list);
}, [list]);

useEffect(() => {
  console.log('data', data);
}, [data]);

我对useEffect监控的理解应该是,当data被更改时,他会重新执行useEffect中的代码.然而,我实际上发现这样的写作会导致无限的重新渲染.因此,我不明白为什么会有一个无休止的循环.

我试图注释中间的代码段

useEffect(() => {
  if (data !== null) {
    let arr = [];
    for (var i = 0; i < (data as DeviceDataOne[]).length; i += 4) {
      arr.push((data as DeviceDataOne[]).slice(i, i + 4));
    }
    setList(JSON.parse(JSON.stringify(arr)));
  } else {
    setList([]);
  }
}, [data]);

但我不明白为什么我不能这样写,也不明白是什么导致了这样的问题.

推荐答案

我对useEffect监控的理解应该是,当 数据被更改时,他将在useEffect中重新执行代码.

您对useEffect挂钩的理解是正确的,但您没有理解useSelector挂钩可能会在组件每次呈现时返回一个新的数组对象引用.换句话说,data是触发useEffect挂钩运行的新引用.useEffect挂钩将list状态更新入队.当React处理list状态更新时,它触发组件重现器.data是从Redux存储中 Select 和计算的.重复令人作呕的话.

您可以通过将相等函数与useSelector挂钩一起使用来帮助打破循环.请参见Equality Comparisons and Updates.

import { shallowEqual, useSelector } from 'react-redux';

...

const data = useSelector((state: DeviceList) => {
  if (state.basicSetting !== null) {
    return state.basicSetting.filter(
      item =>
        item.deviceName.includes('ac') &&
        !item.deviceName.includes('bz'),
    );
  } else {
    return null;
  }
}, shallowEqual); // <-- shallow equality instead of strict equality

另一个很好的解决方案是从Reselect创建一个有记忆的 Select 器函数,由于您使用的是Redux-Toolkit,因此createSelector将从Reselect中重新导出.(Reselect is maintained by the same Redux/Redux-Toolkit developers)

它允许您写入相同的计算data值,但它会记录结果,因此如果 Select 器的选定状态为into,则 Select 器返回相同的先前计算值.换句话说,它提供了一个稳定的返回data值,只有在状态实际更新并且 Select 器计算新的值引用时才会触发useEffect.

然而,您从存储中 Select STATE并在本地将其复制到STATE中是不太清楚的.这通常被认为是一种react 反模式.您可以直接从store 计算list的值.

示例:

const list = useSelector((state: DeviceList) => {
  if (state.basicSetting === null) {
    return null;
  }
  
  const data = state.basicSetting.filter(
    item =>
      item.deviceName.includes('ac') &&
      !item.deviceName.includes('bz'),
  ) as DeviceDataOne[];

  const list: DeviceDataOne[] = [];
  while (data.length) {
    list.push(data.splice(0, 4));
  }
  return list;
});

useEffect(() => {
  console.log('list', list);
}, [list]);

Reactjs相关问答推荐

cypress 不能点击SPAN

根据另一个Select中的选定值更改Select中的值

如何在单击行中的图标时避免选中ionic 复选框?

无法在主组件的返回语句中呈现预期组件

React:关于useEffect钩子如何工作的困惑

Redux工具包-useSelector如何通过Reducer引用InitialState?

当useEffect和onClick处理程序都调用useCallback函数时,有没有办法对useEffect产生额外的影响?

MUI:在props 主题中找到的值:;错误;是[Object],而不是字符串或数字

强制 useEffect 仅运行一次

Spring Boot + React + MySQL应用的架构层面有哪些?

字段是必需的,当它没有定义时

在 React-Select 中显示多值的倒序

我应该如何将字符串作为props 传递给 React 组件?

部署到github pages时请求路径错误

ReactJS:在导航栏中使用 Select inside Link 组件时如何避免重定向?

我刚刚部署了我的 Vite React 站点,但我的图标/图像没有部署

如何使用 React Router 在 React 中正确同步 useEffect 和 useLocation 挂钩?

NextJs - 如何从站点 map 生成器中删除重复的 URL?

npm init vite@latest 和 npm init vite 有什么区别?

React LinkProps 的含义