React Warning: Can't call setState (or forceUpdate) on an unmounted component
我有两个组件:
订单 - 获取一些数据并显示它。
ErrorHandler - 如果服务器上发生一些错误,模式将显示并显示一条消息。
ErrorHandler 组件正在扭曲订单组件
我正在使用 axios 包来加载 Orders 组件中的数据,并且我使用 axios 拦截器来设置有关错误的状态,并在组件卸载后弹出。
当我来回导航到订单组件时,有时会在控制台中出现错误:
1 2 3 4 | 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 警告来解决它:只能更新已安装或正在安装的组件,但在这里我无法通过检查员制作 axios 令牌。以前有人解决过这个问题吗?
这是我的组件:
订单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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 ( {this.state.orders.map(order => { return (<Order key={order.id} ingrediencies={order.ingrediencies} price={order.price} />); })} ); } } export default ErrorHandler(Orders, api); |
错误处理程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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; |
我认为这是由于触发 setState 的异步调用,即使组件未挂载也可能发生。为了防止这种情况发生,您可以使用某种标志:
1 2 3 4 5 6 7 8 9 | state = { isMounted: false } componentDidMount() { this.setState({isMounted: true}) } componentWillUnmount(){ this.state.isMounted = false } |
然后用 if:
package你的 setState 调用
1 2 3 | if (this.state.isMounted) { this.setState({ loading: false, orders: fetchedOrders }); } |
编辑-添加功能组件示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function Component() { const [isMounted, setIsMounted] = React.useState(false); useEffect(() => { setIsMounted(true); return () => { setIsMounted(false); } }, []); return ; } export default Component; |
您不能在 componentWillMount 方法中设置状态。尝试重新考虑您的应用程序逻辑并将其移至另一个生命周期方法。
我认为根本原因与我昨天回答的相同,您需要"取消"
关于错误处理的说明,它看起来过于复杂,我肯定会鼓励查看 React 提供的 ErrorBoundaries。您不需要
对于 ErrorBoundaries,React 引入了一个生命周期方法,称为:
您可以使用它来将
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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; } } |
然后在你的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | 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 ( {this.state.orders.map(order => { return (<Order key={order.id} ingrediencies={order.ingrediencies} price={order.price} />); })} ); } } |
并在你的代码中使用它:
1 2 3 | <ErrorHandler> <Orders /> </ErrorHandler> |