使用React Router导航时componentDidMount触发两次
解决React Router导航时componentDidMount异步请求触发两次的问题
看起来你遇到的这个场景在React 18开发环境里挺常见的,我帮你拆解下可能的原因和对应的解决方案:
最可能的诱因:React 18的Strict Mode
React 18在开发模式下的StrictMode会故意重复调用组件的生命周期方法(包括componentDidMount),目的是帮你提前检测潜在的副作用问题。刷新页面时,StrictMode的重复调用逻辑可能不会触发,但通过React Router导航进入页面时,组件的挂载流程会被StrictMode拦截并执行两次。
你可以先快速验证这个点:暂时移除项目根组件外层的<StrictMode>标签,再通过导航进入页面,看看请求是否还会触发两次。如果问题消失,那就是StrictMode导致的。
代码中的潜在问题:直接修改实例属性this.config
你的代码里直接修改了this.config这个实例属性,再调用setState,这不符合React的状态管理规范。实例属性的变化不会被React追踪,也不会触发组件重新渲染,而且当组件被重复挂载时,this.config会被重新初始化,容易导致逻辑混乱。
更好的做法是把config放到组件的state中,让状态变化被React统一管理:
export default class EditorWrap extends Component { constructor(props) { super(props) this.state = { loadedS3Credential: false, config: { // 将config移至state内 placeholderText: 'edit here' } } } async componentDidMount() { if (!this.state.loadedS3Credential) { console.log('fire') try { const resp = await axios('/signS3') const imageUploadToS3 = resp.data if (imageUploadToS3) { this.setState({ loadedS3Credential: true, config: { // 更新state中的config ...this.state.config, imageUploadToS3 } }) } } catch (err) { // 不要忽略请求失败的情况 console.error('获取S3凭证失败:', err) } } } render() { if (!this.state.loadedS3Credential) return null return ( <EditorBody config={this.state.config} /> ) } }
其他排查方向
- 检查路由配置:确保
EditorWrap组件没有被多个<Route>标签重复渲染,比如是否存在嵌套路由或路径匹配规则导致组件被多次加载。 - 检查父组件:如果父组件存在不必要的重复渲染,可能会导致子组件(即
EditorWrap)被卸载后重新挂载,从而触发两次componentDidMount。可以给父组件添加React.memo或优化其渲染逻辑来避免。
额外提示:兼容StrictMode的请求逻辑
如果不想移除StrictMode(毕竟它能帮你提前发现潜在问题),可以让异步请求逻辑变成幂等的,或者用实例属性标记请求是否已执行:
export default class EditorWrap extends Component { constructor(props) { super(props) this.state = { loadedS3Credential: false, config: { placeholderText: 'edit here' } } this.hasFetchedCredential = false // 标记请求是否已发起 } async componentDidMount() { if (!this.hasFetchedCredential && !this.state.loadedS3Credential) { this.hasFetchedCredential = true // 请求前标记为已执行 console.log('fire') try { const resp = await axios('/signS3') const imageUploadToS3 = resp.data if (imageUploadToS3) { this.setState({ loadedS3Credential: true, config: { ...this.state.config, imageUploadToS3 } }) } } catch (err) { console.error('获取S3凭证失败:', err) this.hasFetchedCredential = false // 请求失败时重置标记,方便后续重试 } } } // 组件卸载时重置标记,避免内存泄漏或下次挂载的逻辑异常 componentWillUnmount() { this.hasFetchedCredential = false } render() { if (!this.state.loadedS3Credential) return null return ( <EditorBody config={this.state.config} /> ) } }
这样即使StrictMode触发两次componentDidMount,因为this.hasFetchedCredential已经被标记为true,第二次请求不会执行。
内容的提问来源于stack exchange,提问作者Alisa F. Kennedy
相关产品推荐
相关产品推荐

