UseEffect is a commonly used hook that supports two parameters, the first is a callback function and the second is a dependency.
When the second argument is null or undefined, the callback is executed each time render is used, whereas when the second argument is array, the callback is executed only when the dependency changes.
We’re all familiar with this, but how does it work? Let’s find the answer from the source code.
UseEffect is the second parameter
Let’s first test the effect of passing undefined, an empty array, or a dependent array as the second parameter.
Prepare code like this:
import { useEffect, useRef, useState } from 'react';
function Dong() {
const ref = useRef(1);
const [,setState] = useState();
useEffect(() = > {
console.log(111);
});
useEffect(() = > {
console.log(222); } []); useEffect(() = > {
console.log(333);
}, [ref.current]);
useEffect(() = > {
setInterval(() = > {
setState([]);
}, 1000);
setTimeout(() = > {
ref.current = 2;
}, 3000); } []);return <div>dong</div>;
}
Copy the code
We write three useEffect, the second parameter is undefined, [], and there is a dependent array. The callback function prints 111, 222, 333, respectively.
UseState then declares a state that is periodically modified with setInterval to trigger render over and over again.
UseRef declares an object that returns the same object every time we render it. SetTimeout is used to modify its value after 2s.
The results of the implementation should be easy to imagine:
111 prints every time because the second argument is undefined.
222 prints only once because the second parameter is [].
333 prints twice, because the second argument has a dependency that changes once at 2s.
We’re all familiar with this, but why is it that way?
Let’s look at the source code:
UseEffect related source code
React hooks the principle of react hooks:
JSX compiles render function that returns vDOM, but to improve performance, React 16 introduces fiber, which converts vDOM to Fiber and then updates to dom.
The process of converting vDOM to fiber is called Reconcile, and the process of updating dom to FIBER is called commit. The reconcile process is interruptible and requires schedule.
The hooks are also based on Fiber, which maintains a linked list (memorizedState property) on the Fiber node to hold data, with each hook accessing data from the corresponding linked list element.
For example, the 6 hooks of the component above correspond to the 6 elements of the memorizedState list on the Fiber node:
Each hook accesses data on the corresponding linked list element.
This list has a process called mount, followed by only update, so each hook implementation will be divided into two stages: mount and update.
UseEffect:
It is also divided into two functions, mountEffect and updateEffect, which are finally accessed in hook. MemorizedState. This is the general principle of hook.
The second parameter corresponds to deps. How does it determine whether to update?
Let’s focus on this logic:
Deps is the newly passed argument, null if undefined.
Memorizedstate. deps retrieves the previous DEps.
The old and new DEPs are then compared and effect is executed only if true is returned.
The logic for comparison is in areHookInputsEqual:
If prevDeps is null, return false. This is why effect is executed if useEffect is passed either undefined or null as the second argument.
Otherwise, each element in the new and old DEPS array will be compared, and if one element is different, false will be returned.
This explains the difference in effect execution when passing undefined, [], and [dep] in the above example.
There is another way in which an effect can be executed, and this is the logic above:
When the hot update, even if rely on does not change, also need to implement effect, this is controlled by ignorePreviousDependencies variables.
This estimate is not widely known because hot updates are implemented by tools.
We have explained the processing mechanism of the second parameter useEffect from the source level.
In fact, useCallback, useMemo dePS parameter processing logic is the same, source code are similar:
conclusion
UseEffect The second argument is used when undefined, [], [a,b,c] is passed. Undefined is used every time, while dependent arrays are used only when their dependencies change. Empty arrays are used only once.
We explained the reason at the source level:
Hooks access data on the memorizedState property of a Fiber node, and organize a list of hooks that correspond to each other.
The stage of building this list is called mount, and then only need to update, so all hook implementations are divided into mountXxx and updateXxx.
UseEffect determines whether an effect callback is executed by comparing the newly passed DEPs to the previous DePs on the memorizedState when it is updated. It does this:
When dep is null (undefined is also treated as null), it is considered unequal. If it’s hot update, it’s not equal. Otherwise, each dependency of the array is compared to determine equality. Effect is executed whenever the old and new DEPs are not equal.
UseCallback, useMemo dePS processing is the same, we clear from the source level of dePS parameter processing mechanism.