I have a parent component which contains data to build the rows of a table. A child component renders the actual table. Every row should be deletable, so I created a function inside the parent component to update its state and I passed it to the child component, so it could be called on the click of a button.
Even though the setter function is fired the state is not actually changed. The table is not re-rendered and the useEffect which has files as a dependency is not fired.
I'm not understanding why this happens, here's the problem reproduced in codesandbox. I would be very glad if anyone could help solving this.
Edit: I'm adding the code here since links can break over time, as @UmerAbbas pointed out.
// 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>
);
}
CodePudding user response:
Your issue is actually less severe than you think. In your sandbox, it seems you are attempting to update state directly by not creating a copy of files at line 32. Your sandbox works if you do create a shallow copy before making edits to the object:
const _files = { ...files };
For more information, please see here (note that though the documentation applies to class-based components the principle also extends to functional components).
CodePudding user response:
You need to use the function version of set state.
const handleDelete = (file) => {
setFiles((prev) => {
const { [file]: fileName, ...rest } = prev;
return rest;
});
console.log("files set");
};
CodePudding user response:
Try duplicating an element like this:
const handleDelete = (file) => {
const _files = { ...files }; // like this
delete _files[file];
setFiles(_files);
};
