Jak wywołać funkcję ładowania za pomocą React useEffect tylko raz

205

useEffect React hak będzie działać w funkcji przekazany na każdej zmianie. Można to zoptymalizować, aby zadzwonił tylko wtedy, gdy zmienią się pożądane właściwości.

Co zrobić, jeśli chcę wywołać funkcję inicjowania componentDidMounti nie wywoływać jej ponownie w przypadku zmian? Powiedzmy, że chcę załadować jednostkę, ale funkcja ładowania nie potrzebuje żadnych danych z komponentu. Jak możemy to zrobić za pomocą useEffecthaka?

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

W przypadku haków może to wyglądać tak:

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

Odpowiedzi:

391

Jeśli chcesz uruchomić funkcję podaną useEffectpo pierwszym renderowaniu, możesz podać jej pustą tablicę jako drugi argument.

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

  return <div> {/* ... */} </div>;
}
Tholle
źródło
27
Alternatywnie, jeśli istnieją parametry, których używasz do pobierania danych (np. Identyfikator użytkownika), możesz przekazać identyfikator użytkownika w tej tablicy, a jeśli to się zmieni, składnik pobierze dane. Wiele przypadków użycia będzie tak działać.
trixn
4
tak ... więcej na temat pomijania jest udokumentowane tutaj: reagjs.org/docs/…
Melounek
To wydaje się być najprostszą odpowiedzią, ale ESLint narzeka ... zobacz inną odpowiedź na ten wątek stackoverflow.com/a/56767883/1550587
Simon Hutchison
Po prostu przekaż loadDataOnlyOnce do tablicy zależności. Czy to działa?
jpmarks
85

TL; DR

useEffect(yourCallback, []) - wyzwoli oddzwonienie dopiero po pierwszym renderowaniu.

Szczegółowe wyjaśnienie

useEffectdziała domyślnie po każdym renderowaniu komponentu (powodując w ten sposób efekt).

Podczas składania useEffect w swoim komponencie mówisz React, że chcesz uruchomić wywołanie zwrotne jako efekt. React uruchomi efekt po renderowaniu i po wykonaniu aktualizacji DOM.

Jeśli przekażesz tylko wywołanie zwrotne - wywołanie zwrotne będzie uruchamiane po każdym renderowaniu.

Jeśli przekaże drugi argument (tablicę), React uruchomi wywołanie zwrotne po pierwszym renderowaniu i za każdym razem, gdy zmieni się jeden z elementów tablicy. na przykład podczas umieszczania useEffect(() => console.log('hello'), [someVar, someOtherVar])- wywołanie zwrotne będzie uruchamiane po pierwszym renderowaniu i po każdym renderowaniu tegosomeVarsomeOtherVar został zmieniony lub zmieniony.

Po przekazaniu drugiego argumentu pustej tablicy, React porówna po każdym renderowaniu tablicy i zobaczy, że nic się nie zmieniło, w ten sposób wywołując wywołanie zwrotne dopiero po pierwszym renderowaniu.

Edan Chetrit
źródło
68

useMountEffect hook

Uruchamianie funkcji tylko raz po zamontowaniu komponentu jest tak powszechnym wzorcem, że uzasadnia swój własny haczyk, który ukrywa szczegóły implementacji.

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

Użyj go w dowolnym komponencie funkcjonalnym.

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

Informacje o haku useMountEffect

Podczas używania useEffectz drugim argumentem tablicy React uruchomi wywołanie zwrotne po zamontowaniu (początkowym renderowaniu) i po zmianie wartości w tablicy. Ponieważ mijamy pustą tablicę, będzie ona działać dopiero po zamontowaniu.

Ben Carp
źródło
19
Zdecydowanie wolę twoją odpowiedź, ponieważ reguła ESLint „hook-reakcji / wyczerpujące-deps” zawsze zawiedzie na pustych listach zależności. I na przykład słynny szablon tworzenia aplikacji reaguje na tę regułę.
Dynalon
1
Całkowicie zgadzam się z @Dynalon. To powinno być przyjęte rozwiązanie, ponieważ nie koliduje z regułą
ESLint
Dzięki @Dynalon i Mikado68 :-). Decyzja jest przywilejem PO. Zostałem powiadomiony o twoich komentarzach, ale OP nie. Możesz mu to zasugerować, komentując bezpośrednio pytanie.
Ben Carp
2
Teraz możesz użyć, useMountgdy funkcja efektu potrzebuje czegoś z rekwizytów, ale nigdy nie musi działać ponownie, nawet jeśli ta wartość zmieni się bez ostrzeżenia o linijce: useEffect(()=>console.log(props.val),[])będzie brakowało ostrzeżenia o zależności, ale useMount(()=>console.log(props.val))nie spowoduje ostrzeżenia, ale „zadziała”. Nie jestem jednak pewien, czy wystąpi problem z trybem współbieżnym.
HMR
1
Podoba mi się ten :) Powód jak poprzednio; reguła ESLint nie narzeka na to, a jej nazwa jest łatwiejsza do zrozumienia niż pusta tablica
Frexuz
20

Przekaż pustą tablicę jako drugi argument do useEffect. To skutecznie mówi React, cytując dokumenty :

To mówi Reactowi, że twój efekt nie zależy od żadnych wartości z rekwizytów lub stanu, więc nigdy nie musi być uruchamiany ponownie.

Oto fragment, który możesz uruchomić, aby pokazać, że działa:

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
źródło
4

Lubię definiować mountfunkcję, tak samo traktuje EsLint useMounti uważam, że jest to bardziej zrozumiałe.

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

  const unmount = () => {
    console.log('unmounted')
    // ...
  }
  return unmount
}
useEffect(mount, [])
ekologiczny
źródło
-1

Sztuka polega na tym, że useEffect wymaga drugiego parametru.

Drugi parametr to tablica zmiennych, które komponent sprawdzi, aby upewnić się, że zmienił się przed ponownym renderowaniem. Możesz umieścić dowolne fragmenty rekwizytów i podać tutaj stan, który chcesz sprawdzić.

Lub nie wkładaj nic:

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

To zapewni, że useEffect działa tylko raz.

Uwaga z dokumentów:

Jeśli korzystasz z tej optymalizacji, upewnij się, że tablica zawiera wszystkie wartości z zakresu komponentu (takie jak rekwizyty i stan), które zmieniają się w czasie i są używane przez efekt. W przeciwnym razie kod będzie odwoływał się do nieaktualnych wartości z poprzednich renderowań.

Farrukh Malik
źródło
3
Jeśli dosłownie kopiujesz / wklejasz z CSS Tricks, możesz przynajmniej napisać o swoim źródle. css-tricks.com/run-useeffect-only-once
burtyish