使用ContextAPI时如何在子组件提交表单时更新父类state并解决卸载组件更新警告
问题原因与解决方案
问题原因
Warning: Can't perform a React state update on an unmounted component 警告触发的核心原因是:你在
populateCategories中发起的异步http请求返回时,承载该状态的DataProvider组件已经被卸载,此时执行this.setState操作触发了React的保护机制。
修复方案
方案1:添加组件挂载状态判断(改造成本最低)
修改父组件DataProvider的代码,增加挂载标记位,仅当组件处于挂载状态时才执行状态更新:
import React, {createContext,Component} from 'react' import http from './services/httpService' import {MyApiUrl} from './services/httpService' export const GlobalState = createContext() export class DataProvider extends Component{ // 新增挂载标记位 _isMounted = false state={ jobs:[], loadstate:false, } async populateCategories() { const {data:jobs}=await http.get(MyApiUrl+'/api/jobs') // 新增判断:仅组件挂载时才更新状态 if(this._isMounted){ this.setState({jobs}) } console.log('hellow world') return } async componentDidMount(){ this._isMounted = true await this.populateCategories() } // 新增卸载生命周期,修改标记位 componentWillUnmount(){ this._isMounted = false } logPokemon = this.populateCategories.bind(this); render(){ const {jobs}=this.state; const{logPokemon}=this const{children}=this.props return ( <GlobalState.Provider value={{logPokemon,jobs}}> {children} </GlobalState.Provider> ) } }
方案2:添加请求取消逻辑(优化更彻底)
如果使用的http库支持取消请求(比如axios、fetch原生的AbortController),可以在组件卸载时直接取消未完成的请求,从根源避免后续逻辑执行,同时减少无效网络请求:
// 仅展示修改部分代码 async populateCategories() { // 初始化取消控制器 this.controller = new AbortController() try { const {data:jobs}=await http.get(MyApiUrl+'/api/jobs', { signal: this.controller.signal }) if(this._isMounted){ this.setState({jobs}) } } catch (err) { // 忽略请求取消的报错 if(err.name !== 'AbortError') throw err } } componentWillUnmount(){ this._isMounted = false // 卸载时取消未完成的请求 this.controller?.abort() }
子组件调用注意事项
如果子组件提交表单后会立即跳转/卸载,建议调整逻辑等待logPokemon异步执行完成后再执行卸载操作,避免无效请求:
// 子组件提交逻辑示例 handleSubmit = async () => { // 先执行表单提交逻辑 await submitFormData() // 调用context方法刷新数据,等待执行完成后再跳转 const {logPokemon} = this.context; await logPokemon() // 再执行跳转/卸载操作 navigate('/list') }
内容的提问来源于stack exchange,提问作者Spark Ignite
相关产品推荐
相关产品推荐

