React使用jotai更新状态刷新页面触发Cannot update a component警告如何解决
问题触发原因
React 要求组件的渲染过程必须是无副作用的纯函数,禁止在渲染阶段更新其他组件的状态,否则会导致不可预期的渲染行为。
你出现警告的核心原因是:在react-axios提供的<Get>组件的渲染回调函数中,同步调用了jotai的atom更新方法set。当页面刷新初始渲染时,<Get>组件内部的Request实例正处于渲染阶段,此时你直接更新全局的isDownloadAtom,会触发订阅了该atom的AppBar组件立刻重渲染,相当于在一个组件的渲染流程中插入了另一个组件的更新逻辑,违反了React的渲染规则,因此抛出警告。
解决方案
方案1:通过useEffect将更新移入副作用阶段(推荐)
将全局状态的更新逻辑从渲染回调中移出,放到渲染完成后执行的useEffect回调中,符合React最佳实践。
修改后的Quotes组件代码示例:
import { Get } from 'react-axios'; import { useAtom } from "jotai"; import { isDownloadAtom } from "../store/atom"; import { useState, useEffect } from 'react'; import Button from '@mui/material/Button'; import Typography from '@mui/material/Typography'; export default function Quotes() { const [, setIsDownload] = useAtom(isDownloadAtom) // 新增本地状态暂存加载状态 const [localLoading, setLocalLoading] = useState(false) const url = '/random' // 监听本地加载状态变化,更新全局atom useEffect(() => { setIsDownload(localLoading) }, [localLoading, setIsDownload]) return ( <div style={{ padding: 20 }}> <Get url={url}> {(error, response, isLoading, makeRequest, axios) => { if (error) { setLocalLoading(false) return (<div>Something bad happened: {error.message} <button onClick={() => makeRequest({ params: { reload: true } })}>Retry</button></div>) } else if (isLoading) { // 仅更新本地状态,不直接修改全局atom setLocalLoading(true) return (<div>Loading...</div>) } else if (response !== null) { setLocalLoading(false) return ( <div> {/* 原有业务逻辑 */} </div> ) } return null }} </Get> </div> ) }
方案2:将更新延迟到微任务阶段执行(快速修复)
如果不想新增本地状态,可以直接把状态更新的逻辑用queueMicrotask包裹,延迟到当前同步渲染流程结束后再执行,也可以避免警告:
// 原代码中的set(true)改为 queueMicrotask(() => set(true)) // 原代码中的set(false)改为 queueMicrotask(() => set(false))
内容的提问来源于stack exchange,提问作者angus
相关产品推荐
相关产品推荐

