reaguj na haki czyszczenie useEffect () tylko dla componentWillUnmount?

94

Pozwólcie, że wyjaśnię wynik tego kodu, aby łatwo zadać mój problem.

const ForExample = () => {
    const [name, setName] = useState('');
    const [username, setUsername] = useState('');

    useEffect(() => {
        console.log('effect');
        console.log({
            name,
            username
        });

        return () => {
            console.log('cleaned up');
            console.log({
                name,
                username
            });
        };
    }, [username]);

    const handleName = e => {
        const { value } = e.target;

        setName(value);
    };

    const handleUsername = e => {
        const { value } = e.target;

        setUsername(value);
    };

    return (
        <div>
            <div>
                <input value={name} onChange={handleName} />
                <input value={username} onChange={handleUsername} />
            </div>
            <div>
                <div>
                    <span>{name}</span>
                </div>
                <div>
                    <span>{username}</span>
                </div>
            </div>
        </div>
    );
};

Po ForExample componentzamontowaniu zostanie zarejestrowany „efekt”. Jest to związane z componentDidMount().

Za każdym razem, gdy zmienię dane wejściowe, zarówno „efekt”, jak i „wyczyszczony” będą rejestrowane. I odwrotnie, żadna wiadomość nie zostanie zarejestrowana za każdym razem, gdy zmienię nazwę użytkownika, ponieważ dodałem [username]drugi parametr useEffect(). Jest to związane zcomponentDidUpdate()

Na koniec, gdy ForExample componentodmontowanie zostanie zapisane, zostanie zapisane polecenie „wyczyszczone”. Jest to związane z componentWillUnmount().

Wszyscy to wiemy.

Podsumowując, funkcja „wyczyszczona” jest wywoływana za każdym razem, gdy komponent jest ponownie renderowany (obejmuje odmontowanie)

Jeśli chcę, aby ten komponent rejestrował się „wyczyszczony” tylko na moment, w którym jest odmontowywany, wystarczy zmienić drugi parametr useEffect()na [].

Ale jeśli zmienię [username]na [], ForExample componentnie implementuje już componentDidUpdate()danych wejściowych dla nazwy.

Chcę to zrobić, aby komponent obsługiwał zarówno componentDidUpdate()tylko wprowadzanie nazwy, jak i componentWillUnmount(). (rejestrowanie `` wyczyszczone '' tylko na moment, w którym komponent jest odmontowywany)

koo
źródło
4
Możesz mieć 2 oddzielne efekty. Jedna, której podano tablicę, usernamew której jako drugi argument, i jedna, której jako drugi argument podano pustą tablicę.
Tholle
@Tholle Czy masz na myśli 2 oddzielne metody useEffect ()?
koo
1
Tak, to jeden ze sposobów.
Tholle,
1
@Tholle Myślałem, że zostanie to zastąpione przez ostatnią metodę useEffect (). Spróbuję. Dzięki
koo
2
Świetny! Nie ma za co. To zależy od tego, co powinno zrobić czyszczenie. 2 oddzielne efekty nie są złym rozwiązaniem.
Tholle,

Odpowiedzi:

87

Ponieważ czyszczenie nie jest zależne od opcji username, możesz umieścić czyszczenie w oddzielnym, useEffectktóry otrzymuje pustą tablicę jako drugi argument.

Przykład

const { useState, useEffect } = React;

const ForExample = () => {
  const [name, setName] = useState("");
  const [username, setUsername] = useState("");

  useEffect(
    () => {
      console.log("effect");
    },
    [username]
  );

  useEffect(() => {
    return () => {
      console.log("cleaned up");
    };
  }, []);

  const handleName = e => {
    const { value } = e.target;

    setName(value);
  };

  const handleUsername = e => {
    const { value } = e.target;

    setUsername(value);
  };

  return (
    <div>
      <div>
        <input value={name} onChange={handleName} />
        <input value={username} onChange={handleUsername} />
      </div>
      <div>
        <div>
          <span>{name}</span>
        </div>
        <div>
          <span>{username}</span>
        </div>
      </div>
    </div>
  );
};

function App() {
  const [shouldRender, setShouldRender] = useState(true);

  useEffect(() => {
    setTimeout(() => {
      setShouldRender(false);
    }, 5000);
  }, []);

  return shouldRender ? <ForExample /> : null;
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

Tholle
źródło
ładny i czysty przykład. Zastanawiam się jednak. Czy mogę w jakiś sposób wywołać efekt użycia w zmienionej nawigacji, czy też będę musiał przesunąć go w górę w drzewie komponentów? Ponieważ podczas wklejania „wyczyszczonego” useEffect nie widzę tego wyzwalacza.
Fabian Bosler
121

możesz użyć więcej niż jednego useEffect

na przykład, jeśli moja zmienna to data1, mogę użyć tego wszystkiego w moim komponencie

useEffect( () => console.log("mount"), [] );
useEffect( () => console.log("will update data1"), [ data1 ] );
useEffect( () => console.log("will update any") );
useEffect( () => () => console.log("will update data1 or unmount"), [ data1 ] );
useEffect( () => () => console.log("unmount"), [] );
Mehran Motiee
źródło
5
jaka jest różnica między pierwszym a ostatnim efektem useEffect, najpierw funkcja useEffect zostanie wywołana na willmount lub didmount , ostatnio zwrócono funkcję wywołania zwrotnego useEffect z pustą tablicą dlaczego? czy mógłbyś szczegółowo opisać przypadki użycia useEffect, kiedy i jak możemy ich używać?
siluveru kiran kumar
4
@siluverukirankumar Wartość zwracana (funkcja) wywołania zwrotnego jest tym, co jest wywoływane podczas niszczenia (zdarzenie odmontowania). Dlatego ostatnim przykładem jest HOC, natychmiast zwracająca funkcję. Drugi parametr to miejsce, w którym React szukałby zmian, aby ponownie uruchomić to podpięcie. Gdy jest to pusta tablica, będzie działać tylko raz.
Georgy
3
Dzięki @Georgy dostałem to ostatnie użycieEffect zwraca oddzwonienie niewidoczne wyraźnie
siluveru kiran kumar
2
Tak więc, jeśli utworzysz punkt zaczepienia useEffect i zwróci on funkcję ... to kod przed zwróconą funkcją zostanie uruchomiony jako componentDidMount ... a kod w funkcji zwróconej zostanie wywołany dla elementu componentWillUnmount? To trochę zagmatwane, więc upewnij się, że dobrze rozumiem. useEffect (() => {// kod do uruchomienia po zamontowaniu ... return () => {// kod do uruchomienia demontażu}}) Zgadza się?
Maiya
Proponuję przeczytać overreacted.io/a-complete-guide-to-useeffect Myślenie o hakach w cyklach życia nie jest takie słodkie
moto
2
function LegoComponent() {

  const [lego, setLegos] = React.useState([])

  React.useEffect(() => {
    let isSubscribed = true
    fetchLegos().then( legos=> {
      if (isSubscribed) {
        setLegos(legos)
      }
    })
    return () => isSubscribed = false
  }, []);

  return (
    <ul>
    {legos.map(lego=> <li>{lego}</li>)}
    </ul>
  )
}

W powyższym kodzie funkcja fetchLegos zwraca obietnicę. Możemy „anulować” obietnicę, posiadając warunek w zakresie useEffect, uniemożliwiający aplikacji ustawienie stanu po odmontowaniu komponentu.

Ostrzeżenie: nie można przeprowadzić aktualizacji stanu React na niezmontowanym komponencie. Nie można tego robić, ale wskazuje na wyciek pamięci w aplikacji. Aby to naprawić, anuluj wszystkie subskrypcje i zadania asynchroniczne w funkcji czyszczenia useEffect.

Daniel Yap
źródło
1

Zamiast tworzyć zbyt wiele skomplikowanych funkcji i metod, tworzę odbiornik zdarzeń i automatycznie wykonuję montowanie i odmontowywanie, bez martwienia się o zrobienie tego ręcznie. Oto przykład.

//componentDidMount
useEffect( () => {

    window.addEventListener("load",  pageLoad);

    //component will unmount
    return () => {
       
        window.removeEventListener("load", pageLoad);
    }

 });

teraz, gdy ta część jest skończona, po prostu uruchamiam wszystko, co chcę, z funkcji pageLoad w ten sposób.

const pageLoad = () =>{
console.log(I was mounted and unmounted automatically :D)}
jerryurenaa
źródło
0

useEffect są izolowane we własnym zakresie i odpowiednio renderowane. Obraz z https://reactjs.org/docs/hooks-custom.html

wprowadź opis obrazu tutaj

Peter Anthony Duot
źródło
Link do rozwiązania jest mile widziany, ale upewnij się, że Twoja odpowiedź jest przydatna bez niego: dodaj kontekst wokół linku, aby inni użytkownicy mieli pojęcie, co to jest i dlaczego się tam znajduje, a następnie zacytuj najbardziej odpowiednią część strony, którą podałeś. ponowne łącze w przypadku, gdy strona docelowa jest niedostępna. Odpowiedzi, które są niewiele więcej niż linkiem, mogą zostać usunięte .
Богдан Опир