You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:08:51