Cómo solucionar la advertencia de dependencia faltante al usar useEffect React Hook

Resuelto russ asked hace 5 años • 23 respuestas

Con React 16.8.6 (era bueno en la versión anterior 16.8.3), aparece este error cuando intento evitar un bucle infinito en una solicitud de recuperación:

./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

No he podido encontrar una solución que detenga el bucle infinito. Quiero alejarme del consumo useReducer(). Encontré esta discusión [ESLint] Comentarios sobre la regla de pelusa #14920 de 'deps exhaustivos' donde una posible solución es You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.que no estoy seguro de lo que estoy haciendo, por lo que no he intentado implementarla todavía.

Tengo esta configuración actual, React hook useEffect se ejecuta continuamente en bucle infinito/indefinido y el único comentario es sobre useCallback()el que no estoy familiarizado.

Cómo estoy usando actualmente useEffect()(que solo quiero ejecutar una vez al principio, similar a componentDidMount()):

useEffect(() => {
    fetchBusinesses();
  }, []);
const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
russ avatar Apr 25 '19 07:04 russ
Aceptado

Si no está utilizando el método fetchBusinesses en ningún lugar aparte del efecto, simplemente puede moverlo al efecto y evitar la advertencia.

useEffect(() => {
    const fetchBusinesses = () => {
       return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
  fetchBusinesses();
}, []);

Sin embargo, si utiliza fetchBusinesses fuera del efecto, debe tener en cuenta dos cosas

  1. ¿Hay algún problema con que no lo pase fetchBusinessescomo método cuando se usa durante el montaje con su cierre envolvente?
  2. ¿Su método depende de algunas variables que recibe de su cierre adjunto? Este no es tu caso.
  3. En cada renderizado, se volverá a crear fetchBusinesses y, por lo tanto, pasarlo a useEffect causará problemas. Entonces, primero debes memorizar fetchBusinesses si fueras a pasarlo a la matriz de dependencia.

Para resumir, diría que si está usando fetchBusinessesfuera de useEffect, puede deshabilitar la regla; // eslint-disable-next-line react-hooks/exhaustive-depsde lo contrario, puede mover el método dentro de useEffect.

Para deshabilitar la regla la escribirías así

useEffect(() => {
   // other code
   ...
 
   // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) 
Shubham Khatri avatar Apr 25 '2019 17:04 Shubham Khatri

Hay muy buenas opciones para la gestión estatal de bibliotecas si estás creando una nueva aplicación o tienes suficiente flexibilidad. Mira Retroceso.

Sólo para completar:

1. (Dejó de funcionar) Usar la función como useEffectdevolución de llamada

useEffect(fetchBusinesses, [])

2. Declarar función dentrouseEffect()

useEffect(() => {
  function fetchBusinesses() {
    ...
  }
  fetchBusinesses()
}, [])

3. Memoriza conuseCallback()

En este caso, si tiene dependencias en su función, tendrá que incluirlas en la useCallbackmatriz de dependencias y esto activará useEffectnuevamente si los parámetros de la función cambian. Además, es un montón de texto repetitivo... Así que simplemente pase la función directamente a useEffectcomo en 1. useEffect(fetchBusinesses, []).

const fetchBusinesses = useCallback(() => {
  ...
}, [])
useEffect(() => {
  fetchBusinesses()
}, [fetchBusinesses])

4. Argumento predeterminado de la función

Según lo sugerido por Behnam Azimi

No es una buena práctica, pero podría resultar útil en algunos casos.

useEffect((fetchBusinesses = fetchBusinesses) => {
   fetchBusinesses();
}, []);

5. Crea un gancho personalizado

Cree un enlace personalizado y llámelo cuando necesite ejecutar la función solo una vez. Puede que esté más limpio. También puede devolver una devolución de llamada para restablecer y volver a ejecutar la "inicialización" cuando sea necesario.

// customHooks.js
const useInit = (callback, ...args) => {
  const [mounted, setMounted] = useState(false)
  
  const resetInit = () => setMounted(false)

  useEffect(() => {
     if(!mounted) {
        setMounted(true);
        callback(...args);
     }
  },[mounted, callback]);

  return [resetInit]
}

// Component.js
return ({ fetchBusiness, arg1, arg2, requiresRefetch }) => {
  const [resetInit] = useInit(fetchBusiness, arg1, arg2)

  useEffect(() => {
    resetInit()
  }, [requiresRefetch, resetInit]);

6. Desactive la advertencia de eslint

Deshabilitar las advertencias debería ser su último recurso, pero cuando lo haga, es mejor hacerlo en línea y explícitamente , porque los futuros desarrolladores pueden confundirse o crear errores inesperados sin saber que linting está desactivado.

useEffect(() => {
  fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps
jpenna avatar Feb 20 '2020 20:02 jpenna
./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

No es un error de JavaScript/React, sino una advertencia de ESLint (eslint-plugin-react-hooks).

Te está diciendo que el gancho depende de la función fetchBusinesses, por lo que debes pasarlo como una dependencia.

useEffect(() => {
  fetchBusinesses();
}, [fetchBusinesses]);

Podría resultar en invocar la función en cada renderizado si la función se declara en un componente como:

const Component = () => {
  /*...*/

  // New function declaration every render
  const fetchBusinesses = () => {
    fetch('/api/businesses/')
      .then(...)
  }

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

porque cada vez la función se vuelve a declarar con una nueva referencia.

La forma correcta de hacer esto es:

const Component = () => {
  /*...*/

  // Keep the function reference
  const fetchBusinesses = useCallback(() => {
    fetch('/api/businesses/')
      .then(...)
  }, [/* Additional dependencies */])

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

O simplemente defina la función en useEffect.

Más: [ESLint] Comentarios sobre la regla de pelusa n.º 14920 de 'deps exhaustivos'

r g avatar Apr 25 '2019 07:04 r g

Estas advertencias son muy útiles para encontrar componentes que no se actualizan constantemente: ¿ Es seguro omitir funciones de la lista de dependencias? .

Sin embargo, si desea eliminar las advertencias en todo su proyecto, puede agregar esto a su configuración de ESLint:

  {
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/exhaustive-deps": 0
    }
  }
Jordan Daniels avatar Mar 30 '2020 16:03 Jordan Daniels