Реагировать — чтобы исправить, отмените все подписки и асинхронные задачи в функции очистки useEffect

У меня есть следующий код

const Companies = () => {
  const [company, setCompany] = useState(plainCompanyObj);
  const [companiesData, setCompaniesData] = useState([]);
  
  useEffect(() => {
    Call<any, any>({
      url:
        _baseApiUrl +
        "-------api goes here --------",
      method: "GET",
      data: null,
      success: (companies) => {
        setCompaniesData(companies.companies);
      },
      authorization: sessionStorage.getItem("accessToken"),
    });
  }, []);

  return (
    //return JSX
  );
};

Я получаю следующую ошибку: Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

Также просто для уточнения, что тег Call — это метод, импортированный axios, который я использую для обработчика ошибок URL и т. д. Проблема, как предполагает ошибка, находится в разделе useEffect.

Когда именно вы получаете это сообщение об ошибке?

Nemanja Lazarevic 14.12.2020 11:10

@NemanjaLazarevic У меня есть только один useEffect в моем проекте, так что это должно быть так.

Gerald Ylli 14.12.2020 11:13

Вы можете опубликовать код, в котором вы визуализируете компонент <Companies />. Возможно, у вас есть какая-то логика, которая преждевременно размонтирует ваш компонент <Companies />.

Nemanja Lazarevic 14.12.2020 11:19

@NemanjaLazarevic Есть еще один компонент под названием LayoutCompanies, который возвращает Box из MatUI. <Box> <Companies /> </Box>

Gerald Ylli 14.12.2020 11:24

Очевидно, вы не ожидаете, что этот компонент размонтируется (что предполагает ошибка). Чтобы подтвердить, что это так, вы можете добавить return () => {console.info("Component Unmounded")} внизу вашего useEffect. Если после обновления вы видите этот журнал, значит, вам нужно искать виновника где-то вверху в дереве компонентов.

Nemanja Lazarevic 14.12.2020 11:31
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
5
1 624
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Ваша проблема в том, что вы вызываете setCompaniesData, когда компонент уже размонтирован. Таким образом, вы можете попробовать использовать токен отмены, чтобы отменить запрос axios. https://github.com/axios/axios#cancellation

const Companies = () => {
  const [company, setCompany] = useState(plainCompanyObj);
  const [companiesData, setCompaniesData] = useState([]);
  
  useEffect(() => {
    const cancelTokenSource = axios.CancelToken.source();

    Call<any, any>({
      url:
        _baseApiUrl +
        "-------api goes here --------",
      method: "GET",
      data: null,
      cancelToken: cancelTokenSource.token
      success: (companies) => {
        setCompaniesData(companies.companies);
      },
      authorization: sessionStorage.getItem("accessToken"),
    });
  }, []);

  return () => {
       cancelTokenSource.cancel();
  }
};

Второй вариант — отслеживать смонтированное состояние компонента и вызывать setState только в том случае, если компонент все еще смонтирован. Посмотрите это видео здесь: https://thewikihow.com/video__TleXX0mxaY&ab_channel=LeighHalliday

Проблема, с которой вы столкнулись, заключается в том, что вы создаете запрос на сервер, в то время как пользователь/вы размонтируете (измените представление/перерисуете) компонент, ожидающий данных с сервера.

Отмена запросов ajax - это хорошая практика, а не просто реакция.

Axios использует токены отмены для отмены — отмена

По моему опыту лучше обернуть целые аксиомы в свой собственный код. И обрабатывать отмену там. (что-то вроде api.get(/route); api.cancel(/route))

Надеюсь, поможет

Вот общий пример json axios fetch с отменой асинхронной задачи (Live demo):

import React, { useEffect, useState } from "react";
import { CPromise, CanceledError } from "c-promise2";
import cpAxios from "cp-axios";

function MyComponent(props) {
  const [text, setText] = useState("fetching...");

  useEffect(() => {
    console.info("mount");
    const promise = CPromise.from(function* () {
      try {
        const response = yield cpAxios(props.url);
        setText(`Success: ${JSON.stringify(response.data)}`);
      } catch (err) {
        console.warn(err);
        CanceledError.rethrow(err); //passthrough
        // handle other errors than CanceledError
        setText(`Failed: ${err}`);
      }
    });

    return () => {
      console.info("unmount");
      promise.cancel(); // cancel async task
    };
  }, [props.url]);

  return <p>{text}</p>;
}

Другие вопросы по теме