React表单提交后出现未挂载组件状态更新警告,axios取消令牌无效如何解决?
问题根因排查方向
- 取消令牌逻辑存在缺陷
如果你使用的是函数组件,类组件专属的componentWillUnmount生命周期不会生效,必须将取消逻辑放在useEffect的清理回调中。
其次你的catch分支没有区分「请求取消错误」和「业务错误」,axios主动取消请求后会抛出类型为Cancel的错误,此时如果组件已经卸载,你直接调用setLastError更新组件本地状态,就会触发警告。
解决方案:在catch中先判断错误类型,过滤取消请求的场景:
.catch(err => { if (!axios.isCancel(err)) { setLastError(err.response.data); } })同时不要把
source直接声明在组件顶层作用域,否则多次提交表单时source实例会被覆盖,导致之前的请求无法被正确取消,应该在每次发起请求前重新生成source实例。- 取消令牌逻辑存在缺陷
- addUser实际是异步操作
你默认addUser是同步的Redux普通action,但如果项目使用了redux-thunk/redux-saga等异步中间件,addUser很可能是异步thunk函数,内部存在接口请求、定时器等异步逻辑,等异步逻辑执行完更新Redux状态时,当前登录组件已经卸载。
如果你的登录组件通过useSelector订阅了addUser修改的那部分Redux状态,状态更新时就会触发已卸载组件的重渲染,触发警告。
- addUser实际是异步操作
- 执行顺序问题
你当前的回调逻辑顺序是addUser→history.push,如果请求返回时组件已经卸载,先执行addUser触发状态订阅更新,此时跳转还未执行,就会触发警告。可以调整执行顺序,先跳转再执行状态更新操作:
.then(response => { localStorage.setItem('token', JSON.stringify(response.data.token)); history.push('/'); addUser({ id: response.data.id, name: response.data.username }); })- 执行顺序问题
通用兜底解决方案
不管是哪种场景,都可以加组件挂载状态标记,避免卸载后的状态更新:
如果是类组件:
class Login extends React.Component { _isMounted = false; componentDidMount() { this._isMounted = true; } componentWillUnmount() { this._isMounted = false; source.cancel(); } // 回调中先判断标记再执行状态更新 formSubmitHandler = (e) => { // ...省略其他逻辑 axios.post('/signin', { email: inputs.email, password: inputs.password }, { cancelToken: source.token }) .then(response => { if (!this._isMounted) return; // ...省略其他逻辑 }) .catch(err => { if (!this._isMounted || axios.isCancel(err)) return; setLastError(err.response.data); }) } }
如果是函数组件:
const Login = () => { const isMounted = useRef(true); useEffect(() => { return () => { isMounted.current = false; source.cancel(); } }, []) // 回调中先判断isMounted.current再执行状态更新 }
内容的提问来源于stack exchange,提问作者Colt83
相关产品推荐
相关产品推荐

