Me está costando entender la regla de pelusa de 'profundidades exhaustivas'.
Ya leí este post y este post pero no pude encontrar una respuesta.
Aquí hay un componente React simple con el problema de la pelusa:
const MyCustomComponent = ({onChange}) => { const [value, setValue] = useState(''); useEffect(() => { onChange(value); }, [value]); return ( <input value={value} type='text' onChange={(event) => setValue(event.target.value)}> </input> ) }
Requiere que agregue onChange
a la matriz de dependencias useEffect
. Pero, según tengo entendido, onChange
nunca cambiará, por lo que no debería estar allí.
Normalmente lo manejo así:
const MyCustomComponent = ({onChange}) => { const [value, setValue] = useState(''); const handleChange = (event) => { setValue(event.target.value); onChange(event.target.value) } return ( <input value={value} type='text' onChange={handleChange}> </input> ) }
¿Por qué la pelusa? ¿Alguna explicación clara sobre la regla de pelusa para el primer ejemplo?
¿O no debería usar useEffect
aquí? (Soy un novato con ganchos)
La razón por la que la regla linter quiere que onChange
entre en el gancho useEffect
es porque es posible que onChange
cambie entre representaciones, y la regla lint está destinada a evitar ese tipo de referencia de "datos obsoletos".
Por ejemplo:
const MyParentComponent = () => { const onChange = (value) => { console.log(value); } return <MyCustomComponent onChange={onChange} /> }
Cada renderizado de MyParentComponent
pasará una función onChange
diferente a MyCustomComponent
.
En su caso específico, probablemente no le importe: solo desea llamar a onChange
cuando cambia el valor, no cuando cambia la función onChange
. Sin embargo, eso no está claro por cómo está usando useEffect
.
La raíz aquí es que su useEffect
es algo unidiomático.
useEffect
se usa mejor para los efectos secundarios, pero aquí lo estás usando como una especie de concepto de "suscripción", como: "haz X cuando Y cambia". Eso funciona funcionalmente, debido a la mecánica de la matriz de deps
(aunque en este caso también está llamando a onChange
en el renderizado inicial, lo que probablemente no sea deseado), pero no es el propósito previsto.
Llamar onChange
realmente no es un efecto secundario aquí, es solo un efecto de activar el evento onChange
para <input>
. Así que creo que su segunda versión que llama tanto a onChange
como a setValue
juntos es más idiomática.
Si hubiera otras formas de establecer el valor (por ejemplo, un botón borrar), tener que recordar constantemente llamar a onChange
podría ser tedioso, por lo que podría escribir esto como:
const MyCustomComponent = ({onChange}) => { const [value, _setValue] = useState(''); // Always call onChange when we set the new value const setValue = (newVal) => { onChange(newVal); _setValue(newVal); } return ( <input value={value} type='text' onChange={e => setValue(e.target.value)}></input> <button onClick={() => setValue("")}>Clear</button> ) }
Pero en este punto esto es una división de pelos.
El objetivo principal de la advertencia de exhaustive-deps
es evitar que los desarrolladores pasen por alto dependencias dentro de su efecto y pierdan algo de comportamiento.
Dan abramov, desarrollador del núcleo de Facebook, recomienda enfáticamente mantener esa regla habilitada .
Para el caso de pasar funciones como dependencias, hay un capítulo dedicado en las preguntas frecuentes de React :
https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies
Si tiene que poner una función dentro de su matriz de dependencias:
useCallback
. La referencia se cambiará solo si cambian las dependencias de la función de devolución de llamada.