[英]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.