Cómo llamar a la función de carga con React useEffect solo una vez

205

El gancho useEffect React ejecutará la función pasada en cada cambio. Esto se puede optimizar para permitir que llame solo cuando cambien las propiedades deseadas.

¿Qué sucede si deseo llamar a una función de inicialización componentDidMounty no volver a llamarla en los cambios? Digamos que quiero cargar una entidad, pero la función de carga no necesita ningún dato del componente. ¿Cómo podemos hacer esto usando el useEffectgancho?

class MyComponent extends React.PureComponent {
    componentDidMount() {
        loadDataOnlyOnce();
    }
    render() { ... }
}

Con ganchos esto podría verse así:

function MyComponent() {
    useEffect(() => {
        loadDataOnlyOnce(); // this will fire on every change :(
    }, [...???]);
    return (...);
}
Dávid Molnár
fuente

Respuestas:

390

Si solo desea ejecutar la función dada useEffectdespués del render inicial, puede darle una matriz vacía como segundo argumento.

function MyComponent() {
  useEffect(() => {
    loadDataOnlyOnce();
  }, []);

  return <div> {/* ... */} </div>;
}
Tholle
fuente
27
Alternativamente, si hay parámetros que usa para obtener los datos (por ejemplo, una identificación de usuario), puede pasar la identificación de usuario en esa matriz y si cambia, el componente volverá a obtener los datos. Muchos de los casos de uso funcionarán así.
Trixn
44
sí ... más información sobre la omisión se documenta aquí: reactjs.org/docs/…
Melounek
Esta parece ser la respuesta más simple, pero ESLint se queja ... ver otra respuesta en este hilo stackoverflow.com/a/56767883/1550587
Simon Hutchison
Simplemente pase loadDataOnlyOnce en la matriz de dependencias. ¿Eso funciona?
jpmarks
85

TL; DR

useEffect(yourCallback, []) - activará la devolución de llamada solo después del primer render.

Explicación detallada

useEffectse ejecuta por defecto después de cada renderizado del componente (causando así un efecto).

Al colocar useEffecten su componente, le dice a React que desea ejecutar la devolución de llamada como efecto. React ejecutará el efecto después de renderizar y después de realizar las actualizaciones DOM.

Si solo pasa una devolución de llamada, la devolución de llamada se ejecutará después de cada renderizado.

Si pasa un segundo argumento (matriz), React ejecutará la devolución de llamada después del primer render y cada vez que se cambie uno de los elementos de la matriz. por ejemplo, al realizar useEffect(() => console.log('hello'), [someVar, someOtherVar])la llamada: la devolución de llamada se ejecutará después del primer renderizado y después de cualquier renderizado que haya cambiado someVaro que haya someOtherVarcambiado.

Al pasar el segundo argumento a una matriz vacía, React comparará después de cada renderización de la matriz y verá que no se cambió nada, por lo que llamará a la devolución de llamada solo después de la primera representación.

Edan Chetrit
fuente
68

gancho useMountEffect

Ejecutar una función solo una vez después del montaje de componentes es un patrón tan común que justifica un gancho propio que oculta los detalles de implementación.

const useMountEffect = (fun) => useEffect(fun, [])

Úselo en cualquier componente funcional.

function MyComponent() {
    useMountEffect(function) // function will run only once after it has mounted. 
    return <div>...</div>;
}

Sobre el gancho useMountEffect

Cuando se usa useEffectcon un segundo argumento de matriz, React ejecutará la devolución de llamada después del montaje (representación inicial) y después de que los valores en la matriz hayan cambiado. Como pasamos una matriz vacía, se ejecutará solo después del montaje.

Ben Carp
fuente
19
Prefiero su respuesta, ya que la regla ESLint "react-hooks / exhaustive-deps" siempre fallará en las listas de dependencia vacías. Y, por ejemplo, la famosa plantilla create-react-app aplicará esa regla.
Dynalon
1
Totalmente de acuerdo con @Dynalon. Esta debería ser la solución aceptada, ya que no interfiere con la regla
ESLint
Gracias @ Dynalon y Mikado68 :-). La decisión es un privilegio de la OP. Fui notificado sobre sus comentarios, pero el OP no. Se lo puede sugerir al comentar directamente sobre la pregunta.
Ben Carp
2
Ahora puede usar useMountcuando su función de efecto necesita algo de accesorios pero nunca necesita ejecutarse nuevamente, incluso si ese valor cambia sin advertencia de linter: useEffect(()=>console.log(props.val),[])faltará una advertencia de dependencia pero useMount(()=>console.log(props.val))no causará una advertencia pero "funciona". Sin embargo, no estoy seguro de si habrá un problema con el modo concurrente.
HMR
1
Me gusta este :) La misma razón que el estado anterior; la regla ESLint no se quejará de esta, además el nombre es más fácil de entender que una matriz vacía
Frexuz
20

Pase una matriz vacía como segundo argumento a useEffect. Esto efectivamente le dice a React, citando los documentos :

Esto le dice a React que su efecto no depende de ningún valor de accesorios o estado, por lo que nunca necesita volver a ejecutarse.

Aquí hay un fragmento que puede ejecutar para mostrar que funciona:

function App() {
  const [user, setUser] = React.useState(null);

  React.useEffect(() => {
    fetch('https://randomuser.me/api/')
      .then(results => results.json())
      .then(data => {
        setUser(data.results[0]);
      });
  }, []); // Pass empty array to only run once on mount.
  
  return <div>
    {user ? user.name.first : 'Loading...'}
  </div>;
}

ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>

<div id="app"></div>

Yangshun Tay
fuente
4

Me gusta definir una mountfunción, engaña a EsLint de la misma manera useMounty me parece más explicativo.

const mount = () => {
  console.log('mounted')
  // ...

  const unmount = () => {
    console.log('unmounted')
    // ...
  }
  return unmount
}
useEffect(mount, [])
ecoologico
fuente
0

El truco es que useEffect toma un segundo parámetro.

El segundo parámetro es una matriz de variables que el componente verificará para asegurarse de que haya cambiado antes de volver a renderizar. Podrías poner cualquier trozo de utilería y el estado que quieras aquí para verificarlo.

O no ponga nada:

import React, { useEffect } from 'react';

function App() {
  useEffect(() => {

    // Run! Like go get some data from an API.

  }, []); //Empty array as second argument

  return (
    <div>
      {/* Do something with data. */}
    </div>
  );
}

Eso asegurará que useEffect solo se ejecute una vez.

Nota de los documentos:

Si usa esta optimización, asegúrese de que la matriz incluye todos los valores del alcance del componente (como accesorios y estado) que cambian con el tiempo y que son utilizados por el efecto. De lo contrario, su código hará referencia a valores obsoletos de renders anteriores.

Farrukh Malik
fuente
3
Si literalmente copia / pega de Trucos CSS, lo menos que puede hacer es acreditar su fuente. css-tricks.com/run-useeffect-only-once
burtyish