Is there an easy way to determine which variable in a useEffect
's dependency array triggers a function re-fire?
Simply logging out each variable can be misleading, if a
is a function and b
is an object they may appear the same when logged but actually be different and causing useEffect fires.
For example:
React.useEffect(() => { // which variable triggered this re-fire? console.log('---useEffect---') }, [a, b, c, d])
My current method has been removing dependency variables one by one until I notice the behavior that causes excessive useEffect calls, but there must be a better way to narrow this down.
Triggers give you the power to determine execution time In a sense, useEffect is a similar idea to a database Hook. A database Hook allows you to perform actions after a database operation. Say you're building a game, and whenever a player's score updates, you also want to update the high score if necessary.
Dependency arrays are a concept that is tightly coupled to hooks in React (thus also to function components). Some hooks, like useEffect and useCallback have 2 arguments. The first one is a callback (a function), and the second one is the dependency array. It takes the form of an array of variables.
Important: the useEffect hook will always run on mount regardless of if there is anything in its dependency array. We probably don't want to actually run this effect on our data when it's undefined (as it will be on initial render) but rather we want to wait until it is populated from the API call.
The warning "React Hook useEffect has a missing dependency" occurs when the useEffect hook makes use of a variable or function that we haven't included in its dependencies array. To solve the error, disable the rule for a line or move the variable inside the useEffect hook.
I ended up taking a little bit from various answers to make my own hook for this. I wanted the ability to just drop something in place of useEffect
for quickly debugging what dependency was triggering useEffect
.
const usePrevious = (value, initialValue) => { const ref = useRef(initialValue); useEffect(() => { ref.current = value; }); return ref.current; };
const useEffectDebugger = (effectHook, dependencies, dependencyNames = []) => { const previousDeps = usePrevious(dependencies, []); const changedDeps = dependencies.reduce((accum, dependency, index) => { if (dependency !== previousDeps[index]) { const keyName = dependencyNames[index] || index; return { ...accum, [keyName]: { before: previousDeps[index], after: dependency } }; } return accum; }, {}); if (Object.keys(changedDeps).length) { console.log('[use-effect-debugger] ', changedDeps); } useEffect(effectHook, dependencies); };
Below are two examples. For each example, I assume that dep2
changes from 'foo' to 'bar'. Example 1 shows the output without passing dependencyNames
and Example 2 shows an example with dependencyNames
.
Example 1
Before:
useEffect(() => { // useEffect code here... }, [dep1, dep2])
After:
useEffectDebugger(() => { // useEffect code here... }, [dep1, dep2])
Console output:
{ 1: { before: 'foo', after: 'bar' } }
The object key '1' represents the index of the dependency that changed. Here, dep1
changed and is the 2nd item in the dependency, or index 1
Example 2
Before:
useEffect(() => { // useEffect code here... }, [dep1, dep2])
After:
useEffectDebugger(() => { // useEffect code here... }, [dep1, dep2], ['dep1', 'dep2'])
Console output:
{ dep2: { before: 'foo', after: 'bar' } }
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