如何解决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 举报,一经查实,本站将立刻删除。