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 (...);
}

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>;
}

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.


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.


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/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

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


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, [])

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.


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.