Deshabilitar *react-hooks/exhaustive-deps* es tóxico

Por

Unsplash

El team de React escribió este plugin de ESLint para hacer cumplir las reglas a la hora de escribir funciones tipo hooks. Entre la lista de reglas está la del chequeo de dependencias — exhaustive-deps — que lanza errores cuando detecta que la función invocada en el hook, depende de una propiedad o estado que debe ser recordado, o incluso pueda quedar obsoleto en el próximo re-pintado de la componente:

React Hook useEffect has a missing dependency: ‘sayHi’. Either include it or remove the dependency array.eslint(react-hooks/exhaustive-deps)

Es difícil mantener este control en la mente mientras escribimos el código, y es parte de la gracia de usar el plugin react-hooks.

Hay ocasiones donde siguiendo lo documentado por React queremos que nuestra función hook se ejecute solo una vez, cuando se monta la componente, como en el siguiente código:

const Greeting = ({name}) => {
  const sayHi = () => {
    console.log('Hi there', name);
  };
  useEffect(() => {
    sayHi();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []); // arreglo vacío - say Hi solo cuando se monta Greeting
};


Y frustra encontrarse con el error de que Greeting tiene una dependencia no declarada. Dado que en teoría no estamos haciendo nada errado la solución suele ser culpar al plugin de ser excesivamente estricto y deshabilitarlo.

Lo que explica que la función sayHi solo va a ejecutarse una vez es que el arreglo de dependencias vacío le indica a React qué esta función no depende de nada relacionado al estado de la componente, por ende no necesita ser ejecutada nuevamente la próxima vez, nada va a cambiar.

Pero de mirar el código, el resultado de la ejecución de sayHi sí depende de la propiedad name, e incluirla en las dependencias no va cambiar el comportamiento esperado — de ejecutarse solo una vez — a no ser que en el tiempo de vida de la componente “algo” le asigne un valor diferente a name, o sea la componente necesita reaccionar y re-pintarse porque su estado interno cambió.

Si el comportamiento deseado fuera ejecutar sayHi solo una vez a pesar que el valor de la propiedad cambia, React documenta acá un recurso para hacerlo usando el hook useRef para guardar el valor, en este caso:

const Greeting = ({name}) => {
  const fixedName = useRef();
  const sayHi = () => {
    console.log('Hi there', fixedName.current);
  };
  useEffect(() => {
    fixedName.current = name
  }); // guarda el valor de name en fixedName yse ejecuta primero.
  useEffect(() => {
    sayHi();
  }, []); // no más error y el effect solo corre una vez.
};

Esta implementación no es ideal, es un recurso. El efecto que guarda el valor de name en fixName.current se ejecuta cada vez que la componente se re-pinta pero el efecto que ejecuta sayHi se ejecuta solo la primera vez., Esto funciona porque además React ejecuta los hooks en el orden en que los encuentra en el código.


Deshabilitar la regla es tóxico porque en codebases grandes es difícil mantener el control de cuándo o cuándo no se deben ejecutar los hooks basado en los cambios de estado de las componentes. La regla expone y ayuda a mantener las dependencias documentando el hook, es fácil mirando en el arreglo entender en qué momentos el code va a ser ejecutado.

Usar un recurso es mejor que deshabilitar la regla porque significa que entiendes el trasfondo de tu implementación y por ende futuros comportamientos inesperados/no-deseados, si fuera el caso.


Como siempre, si tienes comentarios déjanos un comentario en Discord o escríbenos directamente a team@getonbrd.com 🤗.

Lo más reciente en Blog