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

#javascript #jquery #reactjs #react-native #use-effect

#javascript #jquery #reactjs #react-native #использование-эффект

Вопрос:

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

 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 разделе.

Комментарии:

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

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

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

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

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

Ответ №1:

Ваша проблема в том, что вы вызываете 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://www.youtube.com/watch?v=_TleXX0mxaYamp;ab_channel=LeighHalliday

Ответ №2:

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

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

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

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

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

Ответ №3:

Вот общий пример выборки json axios с отменой асинхронной задачи (живая демонстрация):

 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.log("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.log("unmount");
      promise.cancel(); // cancel async task
    };
  }, [props.url]);

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