Предупреждение о реакции: невозможно вызвать setState (или forceUpdate) на отключенном компоненте

У меня 2 компонента:
Заказы - получить данные и отобразить их. ErrorHandler - в случае возникновения ошибки на сервере модальное окно покажет и отобразит сообщение. Компонент ErrorHandler искажает компонент заказа

Я использую пакет axios для загрузки данных в компонент «Заказы», ​​и я использую перехватчики axios для установки состояния ошибки и извлечения после размонтирования компонента.

Когда я перехожу к компонентам заказов назад и вперед, я иногда получаю сообщение об ошибке в консоли:

Warning: Can't call setState (or forceUpdate) 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 the componentWillUnmount method.
    in Orders (at ErrorHandler.jsx:40)
    in Auxiliary (at ErrorHandler.jsx:34)
    in _class2 (created by Route)

Я попытался решить это своим предыдущим случаем React Warning: можно обновить только смонтированный или монтируемый компонент, но здесь я не могу сделать токен axios инспекторами. Кто-нибудь раньше решал эту проблему?

Вот мои компоненты:

Заказы:

import React, { Component } from 'react';
import api from '../../api/api';
import Order from '../../components/Order/Order/Order';
import ErrorHandler from '../../hoc/ErrorHandler/ErrorHandler';

class Orders extends Component {
    state = {
        orders: [],
        loading: true
    }

    componentDidMount() {
        api.get('/orders.json')
            .then(response => {
                const fetchedOrders = [];
                if (response && response.data) {
                    for (let key in response.data) {
                        fetchedOrders.push({
                            id: key,
                            ...response.data[key]
                        });
                    }
                }
                this.setState({ loading: false, orders: fetchedOrders });
            })
            .catch(error => {
                this.setState({ loading: false });
            });
    }

    render() {
        return (
            <div>
                {this.state.orders.map(order => {
                    return (<Order
                        key = {order.id}
                        ingrediencies = {order.ingrediencies}
                        price = {order.price} />);
                })}
            </div>
        );
    }
}

export default ErrorHandler(Orders, api);

ErrorHandler:

import React, { Component } from 'react';
import Auxiliary from '../Auxiliary/Auxiliary';
import Modal from '../../components/UI/Modal/Modal';

const ErrorHandler = (WrappedComponent, api) => {
    return class extends Component {
        requestInterceptors = null;
        responseInterceptors = null;
        state = {
            error: null
        };

        componentWillMount() {
            this.requestInterceptors = api.interceptors.request.use(request => {
                this.setState({ error: null });
                return request;
            });
            this.responseInterceptors = api.interceptors.response.use(response => response, error => {
                this.setState({ error: error });
            });
        }

        componentWillUnmount() {
            api.interceptors.request.eject(this.requestInterceptors);
            api.interceptors.response.eject(this.responseInterceptors);
        }

        errorConfirmedHandler = () => {
            this.setState({ error: null });
        }

        render() {
            return (
                <Auxiliary>
                    <Modal
                        show = {this.state.error}
                        modalClosed = {this.errorConfirmedHandler}>
                        {this.state.error ? this.state.error.message : null}
                    </Modal>
                    <WrappedComponent {...this.props} />
                </Auxiliary>
            );
        }
    };
};

export default ErrorHandler;

использовать componentDidMount

Shubham Agarwal Bhewanewala 31.07.2018 11:04

Не помогло .. до сих пор иногда выдает ошибку ... :(

Or Assayag 31.07.2018 11:06

Создайте React jsFiddle, странно, что такая же ошибка отображается на componentDidMount

Paolo Dell'Aguzzo 31.07.2018 11:09
Поведение ключевого слова "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
3
9 094
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы не можете установить состояние в методе componentWillMount. Попробуйте пересмотреть логику своего приложения и перенести ее в другой метод жизненного цикла.

Я не устанавливаю там состояние, я просто делаю функцию настройки, которая устанавливает состояние, но это происходит после. Но я также попытался изменить его на componentDidMount, и ошибка все еще существует.

Or Assayag 31.07.2018 11:03

Посмотрите на свой ErrorHandler. Вы устанавливаете там состояние. И трассировка стека ошибок сообщает вам следующее:

Javid Askerov 31.07.2018 11:21
Ответ принят как подходящий

Я думаю, что из-за асинхронного вызова, который запускает setState, это может произойти, даже если компонент не смонтирован. Чтобы этого не случилось, вы можете использовать какие-то флаги:

  state = {
    isMounted: false
  }
  componentDidMount() {
      this.setState({isMounted: true})
  }
  componentWillUnmount(){
      this.state.isMounted = false
  }

А позже оберните вызовы setState с if:

if (this.state.isMounted) {
   this.setState({ loading: false, orders: fetchedOrders });
}

Изменить - пример добавления функционального компонента:

function Component() {
  const [isMounted, setIsMounted] = React.useState(false);

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

  return <div></div>;
}

export default Component;

Из документации по реакции: вы не должны вызывать setState () в componentWillUnmount (), потому что компонент никогда не будет повторно отрисован. После того, как экземпляр компонента будет размонтирован, он больше никогда не будет монтироваться.

Jonca33 14.12.2018 16:50

Правильно, пропустил ... Я обновил фиктивный пример

Goran.it 21.12.2018 10:13

Разве это не избавило бы от ошибки, если бы состояние не было задано, и, следовательно, не нарушило бы намеченную функциональность?

milosa 21.05.2019 08:30

Предполагаемая функциональность предназначена только для смонтированных компонентов. Если компонент не смонтирован, значит, функциональность не предусмотрена. Фактически в этом случае единственное жизнеспособное решение - это то, что представлено здесь.

Goran.it 21.05.2019 10:02

@ Goran.it А как насчет функционального компонента?

5ervant - techintel.github.io 27.10.2019 18:17

Я думаю, что основная причина та же, что я ответил вчера, вам нужно «отменить» запрос на unmount, я не вижу, делаете ли вы это для вызова api.get() в компоненте Orders.

Замечание об обработке ошибок: это выглядит слишком сложным, я определенно рекомендую посмотреть ErrorBoundaries, предоставленный React. Вам не нужно иметь interceptors или компонент более высокого порядка.

Для ErrorBoundaries React представил метод жизненного цикла под названием componentDidCatch. Вы можете использовать его для упрощения кода ErrorHandler, чтобы:

class ErrorHandler extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, info) {
    this.setState({ hasError: true, errorMessage : error.message });
  }

  render() {
    if (this.state.hasError) {
      return <Modal 
                    modalClosed = {() => console.info('What do you want user to do? Retry or go back? Use appropriate method logic as per your need.')}>
                    {this.state.errorMessage ? this.state.errorMessage : null}
                </Modal>
    }
    return this.props.children;
  }
}

Затем в вашем компоненте Orders:

class Orders extends Component {
    let cancel;
    state = {
        orders: [],
        loading: true
    }

    componentDidMount() {
        this.asyncRequest = api.get('/orders.json', {
        cancelToken: new CancelToken(function executor(c) {
            // An executor function receives a cancel function as a parameter
            cancel = c;
            })
        })
            .then(response => {
                const fetchedOrders = [];
                if (response && response.data) {
                    for (let key in response.data) {
                        fetchedOrders.push({
                            id: key,
                            ...response.data[key]
                        });
                    }
                }
                this.setState({ loading: false, orders: fetchedOrders });
            })
            .catch(error => {
                this.setState({ loading: false });
                // please check the syntax, I don't remember if it is throw or throw new
                throw error;
            });
    }

    componentWillUnmount() {
       if (this.asyncRequest) {
          cancel();
       }
    }

    render() {
        return (
            <div>
                {this.state.orders.map(order => {
                    return (<Order
                        key = {order.id}
                        ingrediencies = {order.ingrediencies}
                        price = {order.price} />);
                })}
            </div>
        );
    }
}

И используйте его в своем коде как:

<ErrorHandler>
   <Orders />
</ErrorHandler>

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