What I know is that useState will not rerender if we pass the same state, and I test it by myself, but it worked only the first time the component rerendered, with an initial state passed to useState. But in the example below, the useState rerenders the component even if I pass the same reference, also useEffect knows that the state does not changed. The picture with console.logs shows that behavior.
const arr = [
{ name: 'Khaled', id: 0 },
{ name: 'Mohamed', id: 1 },
{ name: 'Ahmed', id: 2 },
{ name: 'Mohsen', id: 3 },
];
function App() {
console.log('num $1');
const [same, setSame] = useState({});
useEffect(() => {
console.log('num $3');
const test = arr.find((el) => el.id === 0);
console.log(Object.is(same, test));
setSame(test);
}, [same]);
console.log('num $2');
return <div>welcome</div>;
}
The component is rendered twice because you have the initial value for the same state which is empty { }. inside the useEffect you update this state with a new object then the useEffect triggers a new value that has been added to the same state. which is the matched object from your array. then the component re-rendered and that's why you got in the console.log the true value.
const arr = [
{ name: 'Khaled', id: 0 },
{ name: 'Mohamed', id: 1 },
{ name: 'Ahmed', id: 2 },
{ name: 'Mohsen', id: 3 },
];
function App() {
console.log('num $1');
const [same, setSame] = useState({}); <= initial value
useEffect(() => {
console.log('num $3');
const test = arr.find((el) => el.id === 0);
console.log(Object.is(same, test)); <= changed from false to true because the new value of same
setSame(test); <= you updated the state
}, [same]); <= the value of same changed from { } to a new value and that's make the component to re-render
console.log('num $2');
return <div>welcome</div>;
}
That's an update for your last comment.
import { useState, useEffect } from "react";
const arr = [
{ name: "Khaled", id: 0 },
{ name: "Mohamed", id: 1 },
{ name: "Ahmed", id: 2 },
{ name: "Mohsen", id: 3 }
];
export default function App() {
const [same, setSame] = useState(arr);
useEffect(() => {
const test = arr.find((el) => el.id === 0);
console.log(same); <= here the same is [] with 4 objecs
setSame(test);
console.log(same); <= here the same is { }
}, [same]);
return <div>welcome</div>;
}
with that. this will lead us that the useEffect once it sees the same get updated. then it will re-render the component as I mentioned before.
here same have the same data. but they are different because the first same is [] and the second one is { }. so that's a change in the state. then useEffect will re-render.
Previous answer explains it perfectly. To not have the state re-render you must initialize it with the original array.
const arr = [
{ name: 'Khaled', id: 0 },
{ name: 'Mohamed', id: 1 },
{ name: 'Ahmed', id: 2 },
{ name: 'Mohsen', id: 3 },
];
function App() {
console.log('num $1');
const [same, setSame] = useState(arr); <= initial value is now original arr
useEffect(() => {
console.log('num $3');
const test = arr.find((el) => el.id === 0);
console.log(Object.is(same, test)); <= changed from false to true because the new value of same
setSame(test); <= you updated the state
}, [same]); <= the value of same changed from { } to a new value
console.log('num $2');
return <div>welcome</div>;
}
Make note that this only works for the reference types. Hook doesn’t perform a comparison on objects or arrays, it simply checks whether the reference changes or not. This does not work for value types.
I'm slightly confused on what exactly you are trying to do, but here's my understanding of this and hopefully an answer to your problem.
Its important to remember that a useEffect() will be called on component mount (first render) no matter what. This is why your component is spitting out the num $3 console log on first render, even though logically you would expect it not to run at-all, as the only setSame() function is within the useEffect() itself.
For the reason the component re-renders after the state is set with a seemingly-identical object, another important thing to remember here (and with JavaScript in general) is referential equality. Consider the following:
{} === {} //false
Even though logically, you would expect two identical objects to evaluate as two equals, as far as JavaScript is concerned, they are not the same. So, when you pass an object into the setSame() function that looks the same as what the same state is at that time, JavaScript considers it a change of state and prompts a re-render in React.
Hope this helps.