Como chamar a função de carregamento com React useEffect apenas uma vez

205

O gancho useEffect React executará a função passada em todas as alterações. Isso pode ser otimizado para permitir a chamada somente quando as propriedades desejadas forem alteradas.

E se eu quiser chamar uma função de inicialização componentDidMounte não chamá-la novamente nas alterações? Digamos que eu queira carregar uma entidade, mas a função de carregamento não precisa de nenhum dado do componente. Como podemos fazer isso usando o useEffectgancho?

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

Com ganchos, pode ser assim:

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

Respostas:

389

Se você quiser executar apenas a função atribuída useEffectapós a renderização inicial, poderá fornecer uma matriz vazia como segundo argumento.

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

  return <div> {/* ... */} </div>;
}
Tholle
fonte
27
Como alternativa, se existem parâmetros que você usa para buscar os dados (por exemplo, um ID do usuário), pode passar o ID do usuário nessa matriz e, se ele mudar, o componente buscará os dados novamente. Muitos dos casos de uso funcionarão assim.
trixn
4
sim ... mais sobre pular está documentado aqui: reactjs.org/docs/…
Melounek
Esta parece ser a resposta mais simples, mas a ESLint reclama ... veja outra resposta neste tópico stackoverflow.com/a/56767883/1550587
Simon Hutchison
85

TL; DR

useEffect(yourCallback, []) - acionará o retorno de chamada somente após a primeira renderização.

Explicação detalhada

useEffecté executado por padrão após cada renderização do componente (causando um efeito).

Ao colocar useEffectseu componente, você diz ao React que deseja executar o retorno de chamada como um efeito. O React executará o efeito após a renderização e após executar as atualizações do DOM.

Se você passar apenas um retorno de chamada - o retorno será executado após cada renderização.

Se passar um segundo argumento (matriz), o React executará o retorno de chamada após a primeira renderização e toda vez que um dos elementos da matriz for alterado. por exemplo, ao colocar useEffect(() => console.log('hello'), [someVar, someOtherVar])- o retorno de chamada será executado após a primeira renderização e após qualquer renderização que uma das someVarou someOtherVarseja alterada.

Ao passar o segundo argumento para uma matriz vazia, o React irá comparar após cada renderização da matriz e verá que nada foi alterado, chamando o retorno de chamada somente após a primeira renderização.

Edan Chetrit
fonte
68

gancho useMountEffect

A execução de uma função apenas uma vez após a montagem do componente é um padrão tão comum que justifica um gancho próprio que oculta os detalhes da implementação.

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

Use-o em qualquer componente funcional.

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

Sobre o gancho useMountEffect

Ao usar useEffectcom um segundo argumento da matriz, o React executará o retorno de chamada após a montagem (renderização inicial) e após a alteração dos valores na matriz. Como passamos uma matriz vazia, ela será executada somente após a montagem.

Ben Carp
fonte
19
Eu prefiro sua resposta, pois a regra ESLint "react-hooks / exaustive-deps" sempre falha nas listas de dependência vazias. E, por exemplo, o famoso modelo create-react-app aplicará essa regra.
Dynalon 24/07/19
1
Concordo totalmente com @Dynalon. Esta deve ser a solução aceita, pois não interfer com a regra ESLint
Mikado68
Obrigado @Dynalon e Mikado68 :-). A decisão é um privilégio do OP. Fui notificado sobre seus comentários, mas o OP não. Você pode sugerir isso comentando diretamente sobre a pergunta.
Ben Carp
2
Agora você pode usar useMountquando sua função de efeito precisar de algo de objetos, mas nunca precisar executar novamente, mesmo que esse valor mude sem useEffect(()=>console.log(props.val),[])aviso de aviso : haverá um aviso de dependência ausente, mas useMount(()=>console.log(props.val))não causará um aviso, mas "funciona". Não tenho certeza se haverá um problema no modo simultâneo.
HMR
1
Eu gosto deste :) O mesmo motivo que o estado anterior; a regra ESLint não vai reclamar sobre essa, além do nome mais fácil de entender do que uma matriz vazia
Frexuz
20

Passe uma matriz vazia como o segundo argumento para useEffect. Isso informa efetivamente o React, citando os documentos :

Isso informa ao React que seu efeito não depende de nenhum valor de adereços ou estado, portanto, ele nunca precisa ser executado novamente.

Aqui está um trecho que você pode executar 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
fonte
4

Eu gosto de definir uma mountfunção, ele engana o EsLint da mesma maneira useMounte acho mais auto-explicativo.

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

  const unmount = () => {
    console.log('unmounted')
    // ...
  }
  return unmount
}
useEffect(mount, [])
ecológico
fonte
0

O truque é que useEffect usa um segundo parâmetro.

O segundo parâmetro é uma matriz de variáveis ​​que o componente verificará para ter certeza de que foi alterado antes da nova renderização. Você pode colocar quaisquer pedaços de adereços e o estado que quiser aqui para verificar.

Ou, não coloque 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>
  );
}

Isso garantirá que o useEffect seja executado apenas uma vez.

Nota dos documentos:

Se você usar essa otimização, verifique se a matriz inclui todos os valores do escopo do componente (como adereços e estado) que mudam com o tempo e são usados ​​pelo efeito. Caso contrário, seu código fará referência a valores obsoletos de renderizações anteriores.

Farrukh Malik
fonte
3
Se você literalmente copiar / colar CSS Tricks, o mínimo que você pode fazer é creditar sua fonte. css-tricks.com/run-useeffect-only-once
burtyish