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

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

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.


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.


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/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

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

-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ń.


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.