ReactJS. Aviso: Não é possível executar uma atualização do estado React em um componente desmontado. Este é um no-op, mas indica um memorando

Bom dia, Habr!

Todos os desenvolvedores do reactjs que lidam com interatividade entre a parte traseira e a frente se encontram mais cedo ou mais tarde, ou se encontraram ou encontrarão o seguinte erro:


Literalmente, acontece assim:
Atenção. Não foi possível atualizar o estado React para um componente desinstalado. Isso não é uma operação, mas indica um vazamento de memória no seu aplicativo. Para corrigir, cancele todas as assinaturas e tarefas assíncronas na função de limpeza useEffect.

De fato, tudo é bem simples, basta prestar atenção às seguintes frases:

  1. Não foi possível executar a atualização do estado
  2. um componente desinstalado;
  3. cancelar todas as assinaturas e tarefas assíncronas
  4. limpeza useEffect


No coração dos ganchos. Vá sob o corte!

Assim:

  1. Todos os programadores do reactjs sabem que condição (estado) e que tal atualização (setState) também. Eu não serei isso.
  2. . :
    1) ;
    2) ;
    3) ;
    — 3 . .
  3. . - , : . , async/await — .
  4. useEffect. return () => {} useEffect. , - , : .




Vamos cometer um erro: digamos que estamos desenvolvendo um site com as descrições de filmes carregadas (não vamos aprofundar na API do moviedb , mas usar um filme específico como base). Temos duas páginas:
Casa

Sobre nós



Os links para as duas páginas estão disponíveis no painel de navegação, por exemplo, no cabeçalho. Na página principal ("Página inicial"), há uma comunicação com a parte traseira para enviar informações sobre o filme.

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


Para exibir informações por meio de uma solicitação, é necessário gravar informações no estado para renderização subsequente:

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


Reproduziremos o erro da seguinte maneira - alternaremos de uma rota para outra, ou seja, estando na página “Sobre nós”, iremos para a página “Página inicial” e imediatamente retornaremos novamente à página “Sobre nós” e voila “ Não é possível executar ... .. ” .

O fato é que o servidor não responde às solicitações instantaneamente; a assincronia é usada para ainda executar a solicitação paralelamente às tarefas necessárias. Mas, no caso de um retorno rápido à página Sobre nós, o componente Início é desmontado, o que significa que o estado desse componente será redefinido, mas a solicitação assíncrona ainda ativará o setMovie, que não está mais lá e gerará um erro. A melhor solução é proibir o uso de atualizações de estado ao desmontar um componente:

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


Todo o código fonte pode ser visto aqui: https://gitlab.com/ImaGadzhiev/react-cant-perform

All Articles