[Feature Request] Introduce useEffectAll hook for collective/transactional dependency updates
Summary
We propose a new built-in hook, useEffectAll, that changes the dependency evaluation model from OR to AND. Instead of executing the side-effect when any dependency changes, useEffectAll fires its callback strictly when every single dependency in the array has mutated compared to its state in the previous render cycle.
Motivation
In complex enterprise applications, data streams or unified configuration states often update asynchronously or incrementally. Currently, useEffect executes if a single element in the dependency array changes. This introduces performance bottlenecks when a high-overhead side-effect (like resetting a WebWorker pool, wiping an IndexedDB cache, or triggering heavy analytical underwriting computations) should only occur during a global paradigm shift meaning a transaction where all watched parameters are updated simultaneously.
Currently, developers must write boilerplate code using multiple useRef containers to preserve past states and manually loop through them to verify unanimous mutation. Providing a native, specialized primitive standardizes this architectural requirement.
Detailed Design
The API signature mirrors useEffect exactly, ensuring zero friction for adoption:
import { useEffectAll } from 'react';
useEffectAll(() => {
// Heavy re-initialization or sync logic here
console.log('All parameters updated simultaneously.');
return () => {
// Teardown logic
};
}, [paramA, paramB, paramC]);
Core Behavior Matrix:
- Initial Render: Establishes the reference baseline; the effect callback does not execute.
- Partial State Update (e.g.,
paramA changes, paramB stays identical): The hook evaluates the mutation array, identifies that the change is not unanimous, updates internal references, and safely bypasses the callback execution.
- Transactional State Update (e.g.,
paramA, paramB, and paramC all change): The hook confirms that 100% of the dependency indices have broken equality with their immediate previous states and triggers the effect callback.
Implementation Considerations & Equality
To match React’s core architecture, the default implementation should utilize Object.is for shallow equality comparisons.
However, since enterprise applications frequently pass objects or arrays as subset properties (e.g., [state.metrics, state.rules]), the documentation should encourage pairing this hook with identity-stable values, memoized primitives, or an explicit structural deep-comparison configuration if handling deeply nested data mutations.
How in Real world developers are implementing it now?
Mimicking real world full watch custom use hook
import { useEffect, useRef } from 'react';
// Native, optimized deep equality checker
function isDeepEqual(a, b) {
if (Object.is(a, b)) return true;
if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) return false;
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) return false;
return a.every((val, index) => isDeepEqual(val, b[index]));
}
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
return keysA.every(key => Object.prototype.hasOwnProperty.call(b, key) && isDeepEqual(a[key], b[key]));
}
/**
* useEffectAll
* Strictly executes the callback ONLY when every single item in the deps array has changed.
*/
export function useEffectAll(effect, deps) {
const prevDepsRef = useRef(null);
useEffect(() => {
// 1. Establish the baseline on initial mount
if (prevDepsRef.current === null) {
prevDepsRef.current = deps;
return;
}
// 2. Strict evaluation: Has every single item broken deep equality?
const allChanged = deps.every((currentDep, index) => {
return !isDeepEqual(currentDep, prevDepsRef.current[index]);
});
// 3. Keep memory fresh for the next render loop
prevDepsRef.current = deps;
// 4. Fire the callback only if condition is completely met
if (allChanged) {
return effect();
}
}, deps);
}
Now this might be wrong , fundamentally may vary with React's principle. But I propose this to be have a steady adaptation among global engineers.
Drawbacks & Alternatives of Current Implementation
- Performance Footgun: If developers pass unstable references that mutate on every render loop, the hook could trigger unexpectedly or execute unnecessary calculations. This risk is equivalent to standard
useEffect misconfigurations.
- Alternative: Developers can continue to build user-land custom hooks utilizing a collection of
useRef instances and an array map loop. However, native implementation allows the React reconciler to optimize dependency checking directly within the fiber lifecycle.
[Feature Request] Introduce
useEffectAllhook for collective/transactional dependency updatesSummary
We propose a new built-in hook,
useEffectAll, that changes the dependency evaluation model from OR to AND. Instead of executing the side-effect when any dependency changes,useEffectAllfires its callback strictly when every single dependency in the array has mutated compared to its state in the previous render cycle.Motivation
In complex enterprise applications, data streams or unified configuration states often update asynchronously or incrementally. Currently,
useEffectexecutes if a single element in the dependency array changes. This introduces performance bottlenecks when a high-overhead side-effect (like resetting a WebWorker pool, wiping an IndexedDB cache, or triggering heavy analytical underwriting computations) should only occur during a global paradigm shift meaning a transaction where all watched parameters are updated simultaneously.Currently, developers must write boilerplate code using multiple
useRefcontainers to preserve past states and manually loop through them to verify unanimous mutation. Providing a native, specialized primitive standardizes this architectural requirement.Detailed Design
The API signature mirrors
useEffectexactly, ensuring zero friction for adoption:Core Behavior Matrix:
paramAchanges,paramBstays identical): The hook evaluates the mutation array, identifies that the change is not unanimous, updates internal references, and safely bypasses the callback execution.paramA,paramB, andparamCall change): The hook confirms that 100% of the dependency indices have broken equality with their immediate previous states and triggers the effect callback.Implementation Considerations & Equality
To match React’s core architecture, the default implementation should utilize
Object.isfor shallow equality comparisons.However, since enterprise applications frequently pass objects or arrays as subset properties (e.g.,
[state.metrics, state.rules]), the documentation should encourage pairing this hook with identity-stable values, memoized primitives, or an explicit structural deep-comparison configuration if handling deeply nested data mutations.How in Real world developers are implementing it now?
Mimicking real world full watch custom use hook
Now this might be wrong , fundamentally may vary with React's principle. But I propose this to be have a steady adaptation among global engineers.
Drawbacks & Alternatives of Current Implementation
useEffectmisconfigurations.useRefinstances and an array map loop. However, native implementation allows the React reconciler to optimize dependency checking directly within the fiber lifecycle.