React useEffect verursacht: Kann keine React-Zustandsaktualisierung für eine nicht gemountete Komponente durchführen

Lesezeit: 9 Minuten

Benutzer-Avatar
Ryan Sam

Beim Abrufen von Daten erhalte ich Folgendes: React-Statusaktualisierung für eine nicht gemountete Komponente kann nicht durchgeführt werden. Die App funktioniert immer noch, aber React deutet darauf hin, dass ich möglicherweise ein Speicherleck verursache.

Dies ist keine Operation, weist jedoch auf ein Speicherleck in Ihrer Anwendung hin. Um das Problem zu beheben, kündigen Sie alle Abonnements und asynchronen Aufgaben in einer useEffect-Bereinigungsfunktion.”

Warum bekomme ich immer wieder diese Warnung?

Ich habe versucht, diese Lösungen zu recherchieren:

https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal

https://developer.mozilla.org/en-US/docs/Web/API/AbortController

aber das gab mir immer noch die Warnung.

const  ArtistProfile = props => {
  const [artistData, setArtistData] = useState(null)
  const token = props.spotifyAPI.user_token

  const fetchData = () => {
    const id = window.location.pathname.split("/").pop()
    console.log(id)
    props.spotifyAPI.getArtistProfile(id, ["album"], "US", 10)
    .then(data => {setArtistData(data)})
  }
  useEffect(() => {
    fetchData()
    return () => { props.spotifyAPI.cancelRequest() }
  }, [])
  
  return (
    <ArtistProfileContainer>
      <AlbumContainer>
        {artistData ? artistData.artistAlbums.items.map(album => {
          return (
            <AlbumTag
              image={album.images[0].url}
              name={album.name}
              artists={album.artists}
              key={album.id}
            />
          )
        })
        : null}
      </AlbumContainer>
    </ArtistProfileContainer>
  )
}

Bearbeiten:

In meiner API-Datei habe ich eine hinzugefügt AbortController() und verwendet a signal damit ich eine Anfrage stornieren kann.

export function spotifyAPI() {
  const controller = new AbortController()
  const signal = controller.signal

// code ...

  this.getArtist = (id) => {
    return (
      fetch(
        `https://api.spotify.com/v1/artists/${id}`, {
        headers: {"Authorization": "Bearer " + this.user_token}
      }, {signal})
      .then(response => {
        return checkServerStat(response.status, response.json())
      })
    )
  }

  // code ...

  // this is my cancel method
  this.cancelRequest = () => controller.abort()
}

Mein spotify.getArtistProfile() sieht aus wie das

this.getArtistProfile = (id,includeGroups,market,limit,offset) => {
  return Promise.all([
    this.getArtist(id),
    this.getArtistAlbums(id,includeGroups,market,limit,offset),
    this.getArtistTopTracks(id,market)
  ])
  .then(response => {
    return ({
      artist: response[0],
      artistAlbums: response[1],
      artistTopTracks: response[2]
    })
  })
}

aber weil mein Signal für einzelne API-Aufrufe verwendet wird, die in a aufgelöst werden Promise.all Ich kann nicht abort() dieses Versprechen, also werde ich immer den Zustand festlegen.

  • Die Warnung ist, weil das Versprechen getArtistProfile() gibt Auflösungen zurück, nachdem die Komponente ausgehängt wurde. Brechen Sie diese Anfrage entweder ab oder fügen Sie, falls dies nicht möglich ist, ein Häkchen in der hinzu .then() Betreuer so setArtistData() wird nicht aufgerufen, wenn die Komponente ausgehängt wurde

    – ᅙᄉᅙ

    2. März 2019 um 1:36 Uhr

  • Es ist nicht möglich zu erklären, warum dies geschieht, ohne mehr über Ihre Anwendung außerhalb dieser Komponente zu wissen. Wir müssen wissen, was diese Komponente zum Mounten/Unmounten veranlasst. Was passiert in der Anwendung, wenn Sie den Fehler erhalten?

    – Ryan Cogwell

    2. März 2019 um 13:23 Uhr

  • @ııı Wie würde ich überprüfen, ob die Komponente nicht bereitgestellt wurde?

    – Ryan Sam

    2. März 2019 um 22:21 Uhr

  • Dies ist kein echtes Speicherleck, sondern höchstwahrscheinlich eine falsche Warnung – weshalb das React-Team die Warnung in der nächsten Version entfernen wird. Sehen PR

    – Kati

    10. Dezember 2021 um 6:11 Uhr

Bei mir hat sauber der Stand beim Unmounten der Komponente geholfen.

 const [state, setState] = useState({});

useEffect(() => {
    myFunction();
    return () => {
      setState({}); // This worked for me
    };
}, []);

const myFunction = () => {
    setState({
        name: 'Jhon',
        surname: 'Doe',
    })
}

  • Ich verstehe die Logik dahinter nicht, aber es funktioniert.

    – Getzel

    18. März 2021 um 0:39 Uhr

  • Erklären Sie bitte jemandem.

    – entithat

    9. Juni 2021 um 23:33 Uhr

  • Oh, ich glaube, ich habe es verstanden. Die Callback-Funktion in useEffect wird nur ausgeführt, wenn die Komponente entladen wird. Deshalb können wir zugreifen name und surname Requisiten des Zustands, bevor die Komponente entladen wird.

    – entithat

    9. Juni 2021 um 23:48 Uhr

  • Wenn Sie eine Funktion von useEffect zurückgeben, wird diese Funktion ausgeführt, wenn die Komponente ausgehängt wird. Wenn Sie dies ausnutzen, setzen Sie Ihren Zustand auf leer. Dadurch bleibt der Status leer, wenn Sie diesen Bildschirm verlassen oder die Komponente ausgehängt wird, sodass die Komponenten Ihres Bildschirms nicht versuchen, erneut zu rendern. ich hoffe das hilft

    – Josef Ajibodu

    29. Juli 2021 um 0:54 Uhr

  • Dies hätte auch funktioniert, wenn Sie eine leere Funktion von useEffect zurückgegeben hätten. React stellt nur sicher, dass Sie eine Funktion von useEffect zurückgeben, um eine Bereinigung durchzuführen. es ist egal, welche Bereinigung Sie durchführen

    – Pankaj

    17. September 2021 um 3:15 Uhr

Benutzer-Avatar
ᅙᄉᅙ

Teilen der AbortController zwischen den fetch() Anfragen ist der richtige Ansatz.
Wann irgendein des Promises werden abgebrochen, Promise.all() wird mit ablehnen AbortError:

function Component(props) {
  const [fetched, setFetched] = React.useState(false);
  React.useEffect(() => {
    const ac = new AbortController();
    Promise.all([
      fetch('http://placekitten.com/1000/1000', {signal: ac.signal}),
      fetch('http://placekitten.com/2000/2000', {signal: ac.signal})
    ]).then(() => setFetched(true))
      .catch(ex => console.error(ex));
    return () => ac.abort(); // Abort both fetches on unmount
  }, []);
  return fetched;
}
const main = document.querySelector('main');
ReactDOM.render(React.createElement(Component), main);
setTimeout(() => ReactDOM.unmountComponentAtNode(main), 1); // Unmount after 1ms
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.development.js"></script>
<main></main>

Benutzer-Avatar
Dzmitri Kulahin

Beispielsweise haben Sie eine Komponente, die einige asynchrone Aktionen ausführt, dann das Ergebnis in state schreibt und den Statusinhalt auf einer Seite anzeigt:

export default function MyComponent() {
    const [loading, setLoading] = useState(false);
    const [someData, setSomeData] = useState({});
    // ...
    useEffect( async () => {
        setLoading(true);
        someResponse = await doVeryLongRequest(); // it takes some time
        // When request is finished:
        setSomeData(someResponse.data); // (1) write data to state
        setLoading(false); // (2) write some value to state
    }, []);

    return (
        <div className={loading ? "loading" : ""}>
            {someData}
            <Link to="SOME_LOCAL_LINK">Go away from here!</Link>
        </div>
    );
}

Angenommen, der Benutzer klickt wann auf einen Link doVeryLongRequest() führt noch aus. MyComponent ist ausgehängt, aber die Anfrage ist noch am Leben und wenn sie eine Antwort erhält, versucht sie, den Status in Zeilen festzulegen (1) und (2) und versucht, die entsprechenden Knoten in HTML zu ändern. Wir erhalten einen Fehler von Betreff.

Wir können es beheben, indem wir prüfen, ob die Komponente noch montiert ist oder nicht. Lassen Sie uns eine erstellen componentMounted ref (Zeile (3) unten) und stellen Sie es ein true. Wenn die Komponente ausgehängt ist, setzen wir sie auf false (Linie (4) unter). Und lassen Sie uns das überprüfen componentMounted Variable jedes Mal, wenn wir versuchen, den Status (line (5) unter).

Der Code mit Korrekturen:

export default function MyComponent() {
    const [loading, setLoading] = useState(false);
    const [someData, setSomeData] = useState({});
    const componentMounted = useRef(true); // (3) component is mounted
    // ...
    useEffect( async () => {
        setLoading(true);
        someResponse = await doVeryLongRequest(); // it takes some time
        // When request is finished:
        if (componentMounted.current){ // (5) is component still mounted?
            setSomeData(someResponse.data); // (1) write data to state
            setLoading(false); // (2) write some value to state
        }
        return () => { // This code runs when component is unmounted
            componentMounted.current = false; // (4) set it to false when we leave the page
        }
    }, []);

    return (
        <div className={loading ? "loading" : ""}>
            {someData}
            <Link to="SOME_LOCAL_LINK">Go away from here!</Link>
        </div>
    );
}

  • Ich bin mir dieser Informationen nicht sicher, aber wenn Sie die Variable „componentMounted“ auf diese Weise festlegen, wird wahrscheinlich die folgende Warnung ausgelöst: „Zuweisungen an die Variable „componentMounted“ aus React Hook useEffect werden nach jedem Rendern verloren gehen. Um den Wert über die Zeit zu erhalten, Speichern Sie es in einem useRef-Hook und behalten Sie den veränderlichen Wert in der Eigenschaft ‚.current‘ bei. …“ In diesem Fall kann es erforderlich sein, es als Status festzulegen, wie hier empfohlen: stackoverflow.com/questions/56155959/…

    – Bonellia

    11. August 2021 um 13:14 Uhr


  • Es ist gültig, aber Sie sollten den useRef-Hook verwenden, um den Wert von zu speichern componentMounted (änderbarer Wert) oder verschieben Sie die Deklaration der componentMounted Variable innerhalb der useEffect

    – Diego Lope Loyola

    13. Oktober 2021 um 5:12 Uhr

  • Einverstanden, Jungs. Fest

    – Dzmitri Kulahin

    13. Oktober 2021 um 11:03 Uhr


  • Benötigt der useEffect keinen asynchronen Rückruf, um den await at zu verwenden someResponse? useEffect(async () => {...},[])

    – Luigi Minardi

    18. März um 16:53 Uhr


  • Danke, Luigi, du hast recht. Fest

    – Dzmitri Kulahin

    20. März um 11:12 Uhr

Benutzer-Avatar
Mertcan Diken

Sie können versuchen, diesen Zustand wie diesen einzustellen und zu überprüfen, ob Ihre Komponente montiert ist oder nicht. Auf diese Weise sind Sie sicher, dass Sie nicht versuchen, etwas abzurufen, wenn Ihre Komponente ausgehängt ist.

const [didMount, setDidMount] = useState(false); 

useEffect(() => {
   setDidMount(true);
   return () => setDidMount(false);
}, [])

if(!didMount) {
  return null;
}

return (
    <ArtistProfileContainer>
      <AlbumContainer>
        {artistData ? artistData.artistAlbums.items.map(album => {
          return (
            <AlbumTag
              image={album.images[0].url}
              name={album.name}
              artists={album.artists}
              key={album.id}
            />
          )
        })
        : null}
      </AlbumContainer>
    </ArtistProfileContainer>
  )

Ich hoffe, das wird Ihnen helfen.

Benutzer-Avatar
Mammutbaum

Ich hatte ein ähnliches Problem mit einem Bildlauf nach oben und die Antwort von @CalosVallejo hat es gelöst 🙂 Vielen Dank!!

const ScrollToTop = () => { 

  const [showScroll, setShowScroll] = useState();

//------------------ solution
  useEffect(() => {
    checkScrollTop();
    return () => {
      setShowScroll({}); // This worked for me
    };
  }, []);
//-----------------  solution

  const checkScrollTop = () => {
    setShowScroll(true);
 
  };

  const scrollTop = () => {
    window.scrollTo({ top: 0, behavior: "smooth" });
 
  };

  window.addEventListener("scroll", checkScrollTop);

  return (
    <React.Fragment>
      <div className="back-to-top">
        <h1
          className="scrollTop"
          onClick={scrollTop}
          style={{ display: showScroll }}
        >
          {" "}
          Back to top <span>&#10230; </span>
        </h1>
      </div>
    </React.Fragment>
  );
};

  • du hast window.addEventListener(“scroll”, checkScrollTop); ist rendern

    – Filip Kovac

    25. Oktober 2021 um 8:53 Uhr


Benutzer-Avatar
Skmak

Dieser Fehler tritt auf, wenn Sie eine Statusaktualisierung für die aktuelle Komponente durchführen, nachdem Sie zu einer anderen Komponente navigiert haben:

zum Beispiel

  axios
      .post(API.BASE_URI + API.LOGIN, { email: username, password: password })
      .then((res) => {
        if (res.status === 200) {
          dispatch(login(res.data.data)); // line#5 logging user in
          setSigningIn(false); // line#6 updating some state
        } else {
          setSigningIn(false);
          ToastAndroid.show(
            "Email or Password is not correct!",
            ToastAndroid.LONG
          );
        }
      })

Im obigen Fall auf Zeile 5 versende ich login Aktion, die den Benutzer im Gegenzug zum Dashboard und damit zum Anmeldebildschirm navigiert, wird jetzt ausgehängt.
Wenn React Native jetzt Zeile 6 erreicht und sieht, dass der Status aktualisiert wird, schreit es laut, wie mache ich das, die login component ist da nicht mehr.

Lösung:

  axios
      .post(API.BASE_URI + API.LOGIN, { email: username, password: password })
      .then((res) => {
        if (res.status === 200) {
          setSigningIn(false); // line#6 updating some state -- moved this line up
          dispatch(login(res.data.data)); // line#5 logging user in
        } else {
          setSigningIn(false);
          ToastAndroid.show(
            "Email or Password is not correct!",
            ToastAndroid.LONG
          );
        }
      })

Verschieben Sie einfach die Aktualisierung des Reaktionszustands nach oben, verschieben Sie Zeile 6 die Zeile 5 nach oben.
Jetzt wird der Status aktualisiert, bevor der Benutzer wegnavigiert wird. GEWINNEN GEWINNEN

  • du hast window.addEventListener(“scroll”, checkScrollTop); ist rendern

    – Filip Kovac

    25. Oktober 2021 um 8:53 Uhr


Benutzer-Avatar
Xelphin

Es gibt viele Antworten, aber ich dachte, ich könnte einfacher demonstrieren, wie das geht abort funktioniert (zumindest wie es das Problem für mich behoben hat):

useEffect(() => {
  // get abortion variables
  let abortController = new AbortController();
  let aborted = abortController.signal.aborted; // true || false
  async function fetchResults() {
    let response = await fetch(`[WEBSITE LINK]`);
    let data = await response.json();
    aborted = abortController.signal.aborted; // before 'if' statement check again if aborted
    if (aborted === false) {
      // All your 'set states' inside this kind of 'if' statement
      setState(data);
    }
  }
  fetchResults();
  return () => {
    abortController.abort();
  };
}, [])

Andere Methoden:
https://medium.com/wesionary-team/how-to-fix-memory-leak-issue-in-react-js-using-hook-a5ecbf9becf8

  • Dies ist ein korrekt zu verifizierendes abgebrochenes Signal

    – sandeep.gosavi

    1. November 2021 um 17:52 Uhr

  • wirkt wie ein Zauber

    – Oder Assayag

    10. Mai um 8:17 Uhr

1015900cookie-checkReact useEffect verursacht: Kann keine React-Zustandsaktualisierung für eine nicht gemountete Komponente durchführen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy