He estado aprendiendo React y leí que la función devuelta de useEffect está destinada a realizar la limpieza y React realiza la limpieza cuando el componente se desmonta.
Así que experimenté un poco con él, pero en el siguiente ejemplo descubrí que se invocaba la función cada vez que el componente se vuelve a renderizar en lugar de solo cuando se desmontó del DOM, es decir, console.log("unmount"); cada vez que el componente se vuelve a renderizar.
¿Porqué es eso?
function Something({ setShow }) { const [array, setArray] = useState([]); const myRef = useRef(null); useEffect(() => { const id = setInterval(() => { setArray(array.concat("hello")); }, 3000); myRef.current = id; return () => { console.log("unmount"); clearInterval(myRef.current); }; }, [array]); const unmount = () => { setShow(false); }; return ( <div> {array.map((item, index) => { return ( <p key={index}> {Array(index + 1) .fill(item) .join("")} </p> ); })} <button onClick={() => unmount()}>close</button> </div> ); } function App() { const [show, setShow] = useState(true); return show ? <Something setShow={setShow} /> : null; }Ejemplo en vivo: https://codesandbox.io/s/vigilant-leavitt-z1jd2
React realiza la limpieza cuando el componente se desmonta.
No estoy seguro de dónde leíste esto, pero esta declaración es incorrecta. React realiza la limpieza cuando las dependencias de ese gancho cambian y el gancho de efecto necesita ejecutarse nuevamente con nuevos valores . Este comportamiento es intencional para mantener la reactividad de la vista a los datos cambiantes. Saliendo del ejemplo oficial, digamos que una aplicación se suscribe a las actualizaciones de estado del perfil de un amigo. Siendo el gran amigo que eres, decides dejar de ser su amigo y hacerte amigo de otra persona. Ahora la aplicación necesita darse de baja de las actualizaciones de estado del amigo anterior y escuchar las actualizaciones de su nuevo amigo. Esto es natural y fácil de lograr con la forma en que funciona useEffect .
useEffect(() => { chatAPI.subscribe(props.friend.id); return () => chatAPI.unsubscribe(props.friend.id); }, [ props.friend.id ])Al incluir la identificación del amigo en la lista de dependencias, podemos indicar que el enlace debe ejecutarse solo cuando cambia la identificación del amigo.
En su ejemplo, ha especificado la array en la lista de dependencias y está cambiando la matriz en un intervalo establecido. Cada vez que cambia la matriz, el enlace se vuelve a ejecutar.
Puede lograr la funcionalidad correcta simplemente eliminando la matriz de la lista de dependencias y usando la versión de devolución de llamada del gancho setState . La versión de devolución de llamada siempre opera en la versión anterior del estado, por lo que no es necesario actualizar el enlace cada vez que cambia la matriz.
useEffect(() => { const id = setInterval(() => setArray(array => [ ...array, "hello" ]), 3000); return () => { console.log("unmount"); clearInterval(id); }; }, []); Algunos comentarios adicionales serían usar la identificación directamente en clearInterval ya que el valor se cierra (captura) cuando crea la función de limpieza. No hay necesidad de guardarlo en una ref.
Los documentos de React tienen una sección de explicación exactamente sobre esto.
En resumen, la razón es que dicho diseño protege contra datos obsoletos y errores de actualización.
El gancho useEffect en React está diseñado para manejar tanto el renderizado inicial como cualquier renderizado posterior ( aquí hay más al respecto ).
Los efectos se controlan a través de sus dependencias, no por el ciclo de vida del componente que los usa.
Cada vez que cambien las dependencias de un efecto, useEffect el efecto anterior y ejecutará el nuevo efecto.
Dicho diseño es más predecible: cada representación tiene su propio efecto de comportamiento independiente (puro) . Esto asegura que la interfaz de usuario siempre muestre los datos correctos (ya que la interfaz de usuario en el modelo mental de React es una captura de pantalla del estado de un render en particular).
La forma en que controlamos los efectos es a través de sus dependencias.
Para evitar que la limpieza se ejecute en cada renderizado, solo tenemos que no cambiar las dependencias del efecto.
En su caso concreto, la limpieza está ocurriendo porque la array está cambiando, es decir, Object.is(oldArray, newArray) === false
useEffect(() => { // ... }, [array]); // ^^^^^ you're changing the dependency of the effectEstás provocando este cambio con la siguiente línea:
useEffect(() => { const id = setInterval(() => { setArray(array.concat("hello")); // <-- changing the array changes the effect dep }, 3000); myRef.current = id; return () => { clearInterval(myRef.current); }; }, [array]); // <-- the array is the effect depMirando el código, podría adivinar que se debe al segundo parámetro [array] . Lo está actualizando, por lo que llamará a una nueva representación. Intente establecer una matriz vacía.
Cada actualización de estado llamará a volver a renderizar y desmontar, y esa matriz está cambiando.