In the following minimal example, a parent component has a data
property and passes data.value
to a child. I am struggling to understand what exactly is going on here with the update strategy:
const MY_DATAVALUE = {
a: 1,
b: 2
};
const DATA = {
value: MY_DATAVALUE
};
function Child(props) {
useEffect(() => {
console.log("child props changed");
}, [props]);
return <h1>Child</h1>;
}
export default function App() {
const [data, setData] = useState(DATA);
useEffect(() => {
console.log("data changed");
}, [data]);
useEffect(() => {
console.log("data.value changed");
}, [data.value]);
function handleButtonClick() {
const newData = {
value: MY_DATAVALUE
};
setData(newData);
}
return (
<>
<button onClick={handleButtonClick}>Button</button>
<Child value={data.value} />
</>
);
}
(See this Codesandbox) Now, when the button is clicked, I think the following happens:
App's
handleButtonClick()
is executed and thedata
-state now refers to a new object. Therefore App's firstuseEffect
(checking fordata
) triggers.However
data.value
still contains the same reference (toMY_DATAVALUE
), therefore App's seconduseEffect
(checking fordata.value
) does not trigger.
BUT: The Child's useEffect
(checking for props
) triggers. Why is that? According to the parent, data.value
did NOT change (Otherwise the second useEffect
would have triggered).
Can you explain to me, why the Childs useEffect
triggers`? How could I find out if the props "really" changed? Would I have to individually check for all prop-keys? Thank you!
useEffect
dependencies will trigger a change if what we provide is different. This happens if we pass the same reference with a different value or if we pass a different reference.
const newData = {
value: MY_DATAVALUE
};
setData(newData);
data
now refers to a different object, while the value
key refers to the same as the previous value.
This means, this hook will trigger:
useEffect(() => {
console.log("data changed");
}, [data]);
While this will not trigger:
useEffect(() => {
console.log("data.value changed");
}, [data.value]);
So far this is what you explained in both points.
In the case of the child, the props
object is a new reference on every render.
For this reason, this hook will always trigger:
useEffect(() => {
console.log("child props changed");
}, [props]);
While this hook would not trigger:
const MY_DATAVALUE = {
a: 1,
b: 2
};
// In Parent...
<Child value={MY_DATAVALUE} />
// In Child...
useEffect(() => {
console.log("child value changed");
}, [props.value]);