微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

useState 导致初始值的浅拷贝

如何解决useState 导致初始值的浅拷贝

我想将数据(保存为状态)传递给绘制该数据的反应组件。该图还应该能够过滤数据。

数据是如下结构的嵌套对象。

{
  "mylog": {
    "entries": [
      {"Bool1": false,"Bool2": true,...},{"Bool1": true,...
    ]
  },"another_log": {...},...
}

我的方法是在图形组件中定义一个名为 filteredData 的状态,将其设置为传递给图形的数据,然后在我想要过滤数据时更新 filteredData 状态。

function App(props) {
  const [data,setData] = useState({...}); // Initial data here

  return (
    <div>
      <Graph data={data} />
    </div>
  );
}

function Graph(props) {
  const [filteredData,setFilteredData] = useState(props.data);

  const filter = () => {
    setFilteredData(data => {
      ...
    });
  }

  return (
    ...
  );
}

然而,当 filteredData 被过滤时,App 组件中的 data 也会被过滤(这会破坏事情)。我曾尝试在几个地方用 {..props.data} 代替 props.data,但这并没有解决。有任何想法吗?提前致谢。

这是一个最小的、可重复的示例:https://codesandbox.io/s/elastic-morse-lwt9m?file=/src/App.js

解决方法

更新本地状态正在改变道具的事实实际上告诉我们你也在改变状态。

data[log].entries = 在您的过滤器中是违规者。

const filter = () => {
  setFilteredData((data) => {
    for (const log in data) {
      data[log].entries = data[log].entries.filter((s) => s.Bool1);
//    ^^^^^^^^^^^^^^^^^^^ Here is the mutation
    }
    return { ...data }; // Copying data ensures React realizes
    // the state has been updated (at least in this component).
  });
};

return { ...data } 部分也是状态未正确更新的信号。这是一种在本地“修复”状态突变的变通方法。

您应该在修改每个嵌套数组或对象之前复制它。

这里有一个更正状态更新的选项,它也将解决道具问题。

setFilteredData((data) => {
  const newData = {...data};

  for (const log in data) {
    newData[log] = { 
      ...newData[log],entries: data[log].entries.filter((s) => s.Bool1)
    }
  }

  return newData;
});

运行示例如下:

const {useState} = React;

function App() {
  const [data,setData] = useState({
    mylog: {
      entries: [{ Bool1: false },{ Bool1: true }]
    }
  });

  return (
    <div>
      <h3>Parent</h3>
      {JSON.stringify(data)}

      <Graph data={data} />
    </div>
  );
}

function Graph(props) {
  const [filteredData,setFilteredData] = useState(props.data);

  const filter = () => {
    setFilteredData((data) => {

      const newData = {...data};

      for (const log in data) {
        newData[log] = { 
          ...newData[log],entries: data[log].entries.filter((s) => s.Bool1)
        }
      }
      return newData;
    });
  };

  return (
    <div>
      <h3>Child</h3>
      <button onClick={filter}>Filter</button>

      {JSON.stringify(filteredData)}
    </div>
  );
}

ReactDOM.render(<App />,document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

,

基元,如整数或字符串,通过它们的值向下传递,而对象数据类型,如数组,通过它们的引用向下传递。 在您的示例中 - data 是通过引用传递的。这使得它可变。

在 React 中 - props 应该是不可变的和自上而下的。这意味着父母可以将它喜欢的任何道具值发送给孩子,但孩子不能修改自己的道具。来自 ReactJS documentation

无论将组件声明为函数还是类,它都必须 永远不要修改自己的道具。

一个解决方案是传递原始 data 对象的副本。

<Graph data={JSON.parse(JSON.stringify(data))} />

更新了 Codepen。你仍在改变道具 - 不是一个好主意,但它有效。

编辑:不推荐使用 JSON.stringify,因为它存在日期和非原始数据类型的问题。在 JS 中深度克隆的其他方法 - How to Deep clone in javascript

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?