I have a simple question that has to do with React rendering. Here's the link to the code sandbox before I explain: https://codesandbox.io/s/list-rerendering-y3iust?file=/src/App.js
Here's the deal, I have an array of objects stored in a parent component called App. Each object has a 'checked' field which I want to toggle on clicking on that object's respective checkbox. I loop through the array of objects and display them each within a Child component. When I click on a checkbox, the handleChange function executes and that list is updated within the parent component, causing App to rerender along with ALL of the Child components. The problem I want to solve is, how can I make it so I only rerender the Child component that was clicked instead of all of them?
I tried using useCallback along with a functional update to the list state, but that didn't do anything and it still rerenders all of the child components instead of the one being toggled. I have a hunch that I'm using useCallback incorrectly, and that a brand new function is being created. I'd like an explanation of how React does it's rerendering when it comes to arrays, comparing the previous array against the new array. I understand that in my code I'm providing a copy of the original list by destructuring it and then putting it inside a new array, which obviously is not a reference to the original list so React sets the copy as the new state:
App.js
import { useCallback, useState } from "react";
import Child from "./Child";
import "./styles.css";
const mockList = [
{ text: "1", id: 1, checked: false },
{ text: "2", id: 2, checked: false },
{ text: "3", id: 3, checked: false },
{ text: "4", id: 4, checked: false },
{ text: "5", id: 5, checked: false }
];
export default function App() {
const [list, setList] = useState(mockList);
const handleChange = useCallback((checked, id) => {
setList((oldList) => {
for (let i = 0; i < oldList.length; i++) {
if (oldList[i].id === id) {
oldList[i].checked = checked;
break;
}
}
return [...oldList];
});
}, []);
return (
<div className="App">
{list.map((item) => (
<Child
key={item.id}
text={item.text}
checked={item.checked}
handleChange={(checked) => handleChange(checked, item.id)}
/>
))}
</div>
);
}
Child.js
const Child = ({ text, checked, handleChange }) => {
console.log("Child rerender");
return (
<div
style={{
display: "flex",
border: "1px solid green",
justifyContent: "space-around"
}}
>
<p>{text}</p>
<input
style={{ width: "20rem" }}
type="checkbox"
checked={checked}
onChange={(e) => handleChange(e.checked)}
/>
</div>
);
};
export default Child;