无法从子组件更新父级的状态
Cannot update parent's state from child component
我有一个父组件,其中包含用于构建 table 行的数据。子组件呈现实际 table。每行都应该删除table,所以我在父组件中创建了一个函数来更新它的状态,并将它传递给子组件,这样它就可以在点击按钮时被调用。
即使 setter 函数被触发,状态实际上并没有改变。 table 不会重新呈现,并且不会触发 files
作为依赖项的 useEffect
。
我不明白为什么会这样,here是codesandbox中重现的问题。如果有人能帮助解决这个问题,我将非常高兴。
编辑:我在此处添加代码,因为链接可能会随着时间的推移而中断,正如@UmerAbbas 指出的那样。
// App.js
import { useEffect, useState } from "react";
import Table from "./Table";
export default function App() {
const [files, setFiles] = useState({
"mario.jpg": {
name: "mario.jpg",
size: 1234
},
"luigi.mkv": {
name: "luigi.mkv",
size: 1234567
}
});
const [rows, setRows] = useState([]);
useEffect(() => {
console.log("files have changed");
setRows([]);
const filesKeys = Object.keys(files);
for (const filename of filesKeys) {
setRows((prevState) => [
...prevState,
{ filename, size: files[filename].size }
]);
}
}, [files]);
const handleDelete = (file) => {
console.log(file);
const _files = files;
delete _files[file];
console.log("Setting files");
setFiles(_files);
console.log("files set");
};
return (
<div className="App">
<Table rows={rows} handleDelete={handleDelete} />
</div>
);
}
// Table.jsx
export default function Table({ rows, handleDelete }) {
return (
<table style={{ width: "100%" }}>
<tr>
<th>Name</th>
<th>Size</th>
<th></th>
</tr>
{rows.map((row) => {
return (
<tr key={row.filename}>
<td>{row.filename}</td>
<td>{row.size}</td>
<td>
<button onClick={() => handleDelete(row.filename)}>Delete</button>
</td>
</tr>
);
})}
</table>
);
}
您需要使用set state功能版本
const handleDelete = (file) => {
setFiles((prev) => {
const { [file]: fileName, ...rest } = prev;
return rest;
});
console.log("files set");
};
尝试像这样复制一个元素:
const handleDelete = (file) => {
const _files = { ...files }; // like this
delete _files[file];
setFiles(_files);
};
您的问题实际上没有您想象的那么严重。在您的沙箱中,您似乎试图通过不在第 32 行创建 files
的副本来直接更新状态。如果您在对对象进行编辑之前创建浅表副本,则您的沙箱可以正常工作:
const _files = { ...files };
有关详细信息,请参阅 here(请注意,尽管文档适用于 class-based 组件,但原则也适用于功能组件)。
我有一个父组件,其中包含用于构建 table 行的数据。子组件呈现实际 table。每行都应该删除table,所以我在父组件中创建了一个函数来更新它的状态,并将它传递给子组件,这样它就可以在点击按钮时被调用。
即使 setter 函数被触发,状态实际上并没有改变。 table 不会重新呈现,并且不会触发 files
作为依赖项的 useEffect
。
我不明白为什么会这样,here是codesandbox中重现的问题。如果有人能帮助解决这个问题,我将非常高兴。
编辑:我在此处添加代码,因为链接可能会随着时间的推移而中断,正如@UmerAbbas 指出的那样。
// App.js
import { useEffect, useState } from "react";
import Table from "./Table";
export default function App() {
const [files, setFiles] = useState({
"mario.jpg": {
name: "mario.jpg",
size: 1234
},
"luigi.mkv": {
name: "luigi.mkv",
size: 1234567
}
});
const [rows, setRows] = useState([]);
useEffect(() => {
console.log("files have changed");
setRows([]);
const filesKeys = Object.keys(files);
for (const filename of filesKeys) {
setRows((prevState) => [
...prevState,
{ filename, size: files[filename].size }
]);
}
}, [files]);
const handleDelete = (file) => {
console.log(file);
const _files = files;
delete _files[file];
console.log("Setting files");
setFiles(_files);
console.log("files set");
};
return (
<div className="App">
<Table rows={rows} handleDelete={handleDelete} />
</div>
);
}
// Table.jsx
export default function Table({ rows, handleDelete }) {
return (
<table style={{ width: "100%" }}>
<tr>
<th>Name</th>
<th>Size</th>
<th></th>
</tr>
{rows.map((row) => {
return (
<tr key={row.filename}>
<td>{row.filename}</td>
<td>{row.size}</td>
<td>
<button onClick={() => handleDelete(row.filename)}>Delete</button>
</td>
</tr>
);
})}
</table>
);
}
您需要使用set state功能版本
const handleDelete = (file) => {
setFiles((prev) => {
const { [file]: fileName, ...rest } = prev;
return rest;
});
console.log("files set");
};
尝试像这样复制一个元素:
const handleDelete = (file) => {
const _files = { ...files }; // like this
delete _files[file];
setFiles(_files);
};
您的问题实际上没有您想象的那么严重。在您的沙箱中,您似乎试图通过不在第 32 行创建 files
的副本来直接更新状态。如果您在对对象进行编辑之前创建浅表副本,则您的沙箱可以正常工作:
const _files = { ...files };
有关详细信息,请参阅 here(请注意,尽管文档适用于 class-based 组件,但原则也适用于功能组件)。