Let's start with my favorite JavaScript expression:
[]==[] // false
Now, let's say what the React doc says about skipping side effects:
You can tell React to skip applying an effect if certain values haven’t changed between re-renders. To do so, pass an array as an optional second argument to useEffect:
useEffect(() => {/* only runs if 'count' changes */}, [count])
Now let's consider the following component which behavior had made me scratch my head:
const App = () => {
const [fruit, setFruit] = React.useState('');
React.useEffect(() => console.log(`Fruit changed to ${fruit}`), [fruit]);
const [fruits, setFruits] = React.useState([]);
React.useEffect(() => console.log(`Fruits changed to ${fruits}`), [fruits]);
return (<div>
<p>
New fruit:
<input value={fruit} onChange={evt => setFruit(evt.target.value)}/>
<button onClick={() => setFruits([...fruits, fruit])}>Add</button>
</p>
<p>
Fruits list:
</p>
<ul>
{fruits.map(f => (<li key={f}>{f}</li>))}
</ul>
</div>)
}
ReactDOM.render(<App/>, document.querySelector('#root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
When adding 'apple', this is what is being logged in the console:
// on first render
Fruit changed to
Fruits changed to
// after each keystroke of 'apple'
Fruit changed to a
Fruit changed to ap
Fruit changed to app
Fruit changed to appl
Fruit changed to apple
// ater clicking on 'add'
Fruits changed to apple
And I don't understand the middle part. After each keystroke, fruits
goes from []
to []
, which are not the same in JS if they refer to different objects. Therefore, I expected some Fruits changed to
to be logged. So my question is:
What is the exact object comparison process used by React to decide on whether or not to skip the effect hook?
A function which is being used to compare objects is practically a polyfill of Object.is
method. You can see it here in the source code:
https://github.com/facebook/react/blob/master/packages/shared/objectIs.js
And here's a function which compares prevDeps
with nextDeps
within useEffect
implementation:
https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiberHooks.new.js#L1427
By the way, Object.is
is mentioned as a comparison algorhitm in the hooks API section of the docs, under useState
.
After each keystroke, fruits goes from [] to []
It seems that you're under the impression that fruits
is re-assigning to a new array
after each key stroke which is not the case.
It is not comparing two new arrays, it is comparing the same label, which points to the same reference in the memory in this particular point of time.
Given:
var arr = [];
We can check if arr
reference has changed over time (if no mutations took place).
simple example:
var arr = [];
var arr2 = arr;
console.log('arr === arr ', arr === arr)
console.log('arr === arr2 ', arr === arr2)
arr = [];
console.log('---- After the change ----');
console.log('arr === arr ', arr === arr)
console.log('arr === arr2 ', arr === arr2)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With