¿Hay una manera fácil de determinar qué variable en la matriz de dependencia de useEffect
desencadena una función que se vuelve a activar?
Simplemente cerrar la sesión de cada variable puede ser engañoso, si a
es una función b
es un objeto, pueden aparecer iguales cuando se registran, pero en realidad son diferentes y provocan incendios de useEffect.
Por ejemplo:
React.useEffect(() => { // which variable triggered this re-fire? console.log('---useEffect---') }, [a, b, c, d])
Mi método actual ha sido eliminar las variables de dependencia una por una hasta que me doy cuenta del comportamiento que causa llamadas useEffect excesivas, pero debe haber una mejor manera de reducir esto.
Terminé tomando un poco de varias respuestas para hacer mi propio gancho para esto. Quería la capacidad de colocar algo en lugar de useEffect
para depurar rápidamente qué dependencia estaba activando 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); };
A continuación se muestran dos ejemplos. Para cada ejemplo, asumo que dep2
cambia de 'foo' a 'bar'. El ejemplo 1 muestra el resultado sin pasar dependencyNames
y el ejemplo 2 muestra un ejemplo con dependencyNames
.
Ejemplo 1
Antes:
useEffect(() => { // useEffect code here... }, [dep1, dep2])
Después:
useEffectDebugger(() => { // useEffect code here... }, [dep1, dep2])
Salida de la consola:
{ 1: { before: 'foo', after: 'bar' } }
La clave de objeto '1' representa el índice de la dependencia que cambió. Aquí, dep1
cambió y es el segundo elemento de la dependencia, o índice 1
Ejemplo 2
Antes:
useEffect(() => { // useEffect code here... }, [dep1, dep2])
Después:
useEffectDebugger(() => { // useEffect code here... }, [dep1, dep2], ['dep1', 'dep2'])
Salida de la consola:
{ dep2: { before: 'foo', after: 'bar' } }
@simbathesailor/use-what-changed
, ¡Funciona de maravilla!Install
con npm/yarn
y --dev
o --no-save
import { useWhatChanged } from '@simbathesailor/use-what-changed';
// (guarantee useEffect deps are in sync with useWhatChanged) let deps = [a, b, c, d] useWhatChanged(deps, 'a, b, c, d'); useEffect(() => { // your effect }, deps);
Crea este bonito gráfico en la consola:
Hay dos culpables comunes:
// Being used like: export function App() { return <MyComponent fetchOptions={{ urlThing: '/foo', headerThing: 'FOO-BAR' }) } export const MyComponent = ({fetchOptions}) => { const [someData, setSomeData] = useState() useEffect(() => { window.fetch(fetchOptions).then((data) => { setSomeData(data) }) }, [fetchOptions]) return <div>hello {someData.firstName}</div> }
La solución en el caso del objeto, si puede, desglose un objeto estático fuera del renderizado del componente:
const fetchSomeDataOptions = { urlThing: '/foo', headerThing: 'FOO-BAR' } export function App() { return <MyComponent fetchOptions={fetchSomeDataOptions} /> }
También puede envolver en useMemo:
export function App() { return <MyComponent fetchOptions={ useMemo( () => { return { urlThing: '/foo', headerThing: 'FOO-BAR', variableThing: hash(someTimestamp) } }, [hash, someTimestamp] ) } /> }
El mismo concepto se aplica a las funciones hasta cierto punto, excepto que puede terminar con cierres obsoletos.
ACTUALIZAR
Después de un poco de uso en el mundo real, hasta ahora me gusta la siguiente solución que toma prestados algunos aspectos de la solución de Retsam:
const compareInputs = (inputKeys, oldInputs, newInputs) => { inputKeys.forEach(key => { const oldInput = oldInputs[key]; const newInput = newInputs[key]; if (oldInput !== newInput) { console.log("change detected", key, "old:", oldInput, "new:", newInput); } }); }; const useDependenciesDebugger = inputs => { const oldInputsRef = useRef(inputs); const inputValuesArray = Object.values(inputs); const inputKeysArray = Object.keys(inputs); useMemo(() => { const oldInputs = oldInputsRef.current; compareInputs(inputKeysArray, oldInputs, inputs); oldInputsRef.current = inputs; }, inputValuesArray); // eslint-disable-line react-hooks/exhaustive-deps };
Esto se puede usar copiando un literal de matriz de dependencia y simplemente cambiándolo para que sea un objeto literal:
useDependenciesDebugger({ state1, state2 });
Esto permite que el registro conozca los nombres de las variables sin ningún parámetro separado para ese propósito.