ReactJS. Avertissement: impossible d'effectuer une mise à jour de l'état React sur un composant non monté. Ceci est un no-op, mais il indique un mémo

Bonjour, Habr!

Tous les développeurs reactjs qui traitent de l'interactivité entre l'arrière et l'avant se rencontrent tôt ou tard, ou se sont rencontrés, ou rencontreront l'erreur suivante:


Littéralement, cela se présente comme suit :
Attention. Impossible de mettre à jour l'état React pour un composant désinstallé. Ce n'est pas une opération, mais cela indique une fuite de mémoire dans votre application. Pour résoudre ce problème, annulez tous les abonnements et tâches asynchrones dans la fonction de nettoyage useEffect.

En fait, tout est assez simple, il suffit de faire attention aux phrases suivantes:

  1. Impossible d'effectuer la mise à jour de l'état
  2. un composant désinstallé;
  3. annuler tous les abonnements et tâches asynchrones
  4. useEffect nettoyage


Au coeur des crochets. Rentrez sous la coupe!

Donc:

  1. Tous les programmeurs reactjs savent quelle condition (état) et qu'une telle mise à jour (setState) aussi. Je ne le serai pas.
  2. . :
    1) ;
    2) ;
    3) ;
    — 3 . .
  3. . - , : . , async/await — .
  4. useEffect. return () => {} useEffect. , - , : .




Faisons une erreur: disons que nous développons un site avec un téléchargement de description de film (nous n'allons pas approfondir l'API moviedb , mais prendre un film spécifique comme base). Nous avons deux pages:
Accueil

À propos de nous



Des liens vers les deux pages sont disponibles dans le panneau de navigation, par exemple, dans l'en-tête. Sur la page principale ("Accueil") il y a une communication avec le dos pour télécharger des informations sur le film.

/src/Pages/HomePage.js
import React, { useState, useEffect } from 'react';

import { MOVIE_DB_GET } from '../config';

const HomePage = () => {
  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(MOVIE_DB_GET);
        const result = await response.json();
        console.log(result, 'result')
      } catch (e) {
        console.error(e.message)
      }
    };

    //  get-     
    fetchData();
  }, []);

  return (
    <main>
      <h2> </h2>
    </main>
  )
};

export default HomePage;


/src/Pages/AboutPage.js

import React from 'react';

const AboutPage = () => {
  return (
    <main>
      <h2> </h2>
      <p>
                
      </p>
    </main>
  )
};

export default AboutPage;


Pour afficher des informations via une requête, il est nécessaire d'écrire des informations dans l'état pour le rendu suivant:

/src/Pages/HomePage.js

import React, { useState, useEffect } from 'react';

import { MOVIE_DB_GET } from '../config';

const HomePage = () => {
  // movie - react-;
  // setMovie -   react-
  const [ movie, setMovie ] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(MOVIE_DB_GET);
        const result = await response.json();
        console.log(result, 'result');
        setMovie(result);
      } catch (e) {
        console.error(e.message)
      }
    };

    //       
    fetchData();
  }, []);

  // descriptionMovie -  ,  view,         movie

  return (
    <main>
      <h2> </h2>
      <p> </p>

      {
        movie ? descriptionMovie() : false
      }
    </main>
  )
};

export default HomePage;


Nous reproduirons l'erreur de la manière suivante - nous passerons d'un itinéraire à un autre, c'est-à-dire que nous sommes sur la page À propos de nous, nous irons à la page d'accueil et reviendrons immédiatement à la page À propos de nous et le tour est joué " Impossible d'effectuer ... .. " .

Le fait est que le serveur ne répond pas aux requêtes instantanément, l'asynchronie est utilisée pour toujours jouer la requête en parallèle avec les tâches nécessaires. Mais dans le cas d'un retour rapide à la page À propos de nous, le composant Home est démonté, ce qui signifie que l'état de ce composant sera réinitialisé, mais la demande asynchrone lancera toujours setMovie, qui n'est plus là et générera une erreur. La meilleure solution consiste à interdire l'utilisation des mises à jour d'état lors du démontage d'un composant:

/src/Pages/HomePage.js

import React, { useState, useEffect } from 'react';

import { MOVIE_DB_GET } from '../config';

const HomePage = () => {
  // movie - react-;
  // setMovie -   react-
  const [ movie, setMovie ] = useState(null);

  useEffect(() => {
    let cleanupFunction = false;
    const fetchData = async () => {
      try {
        const response = await fetch(MOVIE_DB_GET);
        const result = await response.json();
        console.log(result, 'result')

        //     ,    
        if(!cleanupFunction) setMovie(result);
      } catch (e) {
        console.error(e.message)
      }
    };

    fetchData();

    //   useEffect
    return () => cleanupFunction = true;
  }, []);

  // descriptionMovie -  ,  view,         movie

  return (
    <main>
      <h2> </h2>
      <p> </p>

      {
        movie ? descriptionMovie() : false
      }
    </main>
  )
};

export default HomePage;


Total:


Tout le code source peut être consulté ici: https://gitlab.com/ImaGadzhiev/react-cant-perform

All Articles