简体   繁体   English

反应警告:无法在未安装的组件上调用 setState(或 forceUpdate)

[英]React Warning: Can't call setState (or forceUpdate) on an unmounted component

I have 2 components:我有两个组件:
Orders - fetch some data and display it.订单 - 获取一些数据并显示它。
ErrorHandler - In case some error happen on the server, a modal will show and display a message. ErrorHandler - 如果服务器上发生一些错误,模态将显示并显示一条消息。
The ErrorHandler component is warping the order component ErrorHandler 组件正在扭曲订单组件

I'm using the axios package to load the data in the Orders component, and I use axios interceptors to setState about the error, and eject once the component unmounted.我使用 axios 包在 Orders 组件中加载数据,我使用 axios 拦截器设置有关错误的状态,并在组件卸载后弹出。

When I navigate to the orders components back and forward i sometimes get an error in the console:当我前后导航到订单组件时,有时会在控制台中收到错误消息:

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)

I tried to solve it by my previous case React Warning: Can only update a mounted or mounting component but here I can't make an axios token by the inspectors.我试图通过我之前的案例来解决它React Warning: Can only update amounted ormounting component但在这里我不能由检查员制作 axios 令牌。 Has anyone solved this issue before?以前有人解决过这个问题吗?

Here are my components:这是我的组件:

Orders:订单:

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;

I think that's due to asynchronous call which triggers the setState, it can happen even when the component isn't mounted.我认为这是由于触发 setState 的异步调用,即使未安装组件也可能发生。 To prevent this from happening you can use some kind of flags :为了防止这种情况发生,您可以使用某种标志:

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

And later wrap your setState calls with if:然后用 if 包装你的 setState 调用:

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

Edit - adding functional component example:编辑 - 添加功能组件示例:

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

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

  return <div></div>;
}

export default Component;

You can't set state in componentWillMount method.您不能在 componentWillMount 方法中设置状态。 Try to reconsider your application logic and move it into another lifecycle method.尝试重新考虑您的应用程序逻辑并将其移动到另一种生命周期方法中。

I think rootcause is the same as what I answered yesterday, you need to "cancel" the request on unmount , I do not see if you are doing it for the api.get() call in Orders component.我认为根本原因与我昨天回答的相同,您需要在unmount上“取消”请求,我不知道您是否正在为Orders组件中的api.get()调用执行此操作。

A note on the Error Handling, It looks overly complicated, I would definitely encourage looking at ErrorBoundaries provided by React.关于错误处理的说明,它看起来过于复杂,我绝对鼓励查看 React 提供的ErrorBoundaries There is no need for you to have interceptors or a higher order component.您不需要interceptors或更高阶的组件。

For ErrorBoundaries, React introduced a lifecycle method called: componentDidCatch .对于 ErrorBoundaries,React 引入了一个名为: componentDidCatch的生命周期方法。 You can use it to simplify your ErrorHandler code to:您可以使用它来简化您的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.log('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;
  }
}

Then in your Orders Component:然后在您的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>
        );
    }
}

And use it in your code as:并在您的代码中使用它作为:

<ErrorHandler>
   <Orders />
</ErrorHandler>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 警告:无法在React Native中的已卸载组件上调用setState(或forceUpdate) - Warning: Can't call setState (or forceUpdate) on an unmounted component in React Native 反应-警告:无法在未安装的组件上调用setState(或forceUpdate) - React - Warning: Can't call setState (or forceUpdate) on an unmounted component 警告:无法在卸载的组件上调用setState(或forceUpdate) - Warning: Can't call setState (or forceUpdate) on an unmounted component 无法在已卸载的组件上调用setState(或forceUpdate)。 应对 - Can't call setState (or forceUpdate) on an unmounted component. React 如何解决:无法在 React-Native 中的未挂载组件警告上调用 setState(或 forceUpdate)? - How to solve: Can't call setState(or forceUpdate) on an unmounted component warning in React-Native? 无法在卸载的组件上调用setState(或forceUpdate) - Can't call setState (or forceUpdate) on an unmounted component 如何修复“index.js:1446 警告:无法在未安装的组件上调用 setState(或 forceUpdate)...”在 ReactJS 中 - How to fix 'index.js:1446 Warning: Can't call setState (or forceUpdate) on an unmounted component..." in ReactJS 无法在已卸载的组件上调用setState(或forceUpdate)。 react-image-gallery上的内存泄漏 - Can't call setState (or forceUpdate) on an unmounted component. Memory leak on react-image-gallery 无法使用componentDidMount从API提取数据:无法在已卸载的组件上调用setState(或forceUpdate) - Cannot fetch data from an API using componentDidMount: Can't call setState (or forceUpdate) on an unmounted component 有人知道如何解决此错误:无法在已卸载的组件上调用setState(或forceUpdate)吗? - Does somebody know how to fix this error: Can't call setState (or forceUpdate) on an unmounted component…?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM