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

React useEffect空依赖执行两次 实时数据库数据重复打印

问题原因

两个因素共同导致你看到的重复打印、数据重复问题:

  1. React 18+ 开发环境默认开启严格模式:对空依赖的useEffect,严格模式会在开发阶段主动执行「挂载 -> 模拟卸载 -> 重新挂载」的双调用流程,目的是提前暴露没有正确清理副作用的代码问题,这个行为仅在开发环境存在,生产环境不会触发。
  2. 你的状态更新逻辑本身有缺陷:你在遍历数据库返回的快照时,循环调用setUsers往原数组后追加数据,既没有做副作用清理,也没有做状态覆盖,一旦effect重跑,全量数据会被再追加一遍,直接导致数据重复、重复打印。
修复方案

按照以下几点调整代码即可:

  • 不要在遍历快照的循环里反复调用状态更新函数,先把所有数据处理完成后,单次调用setUsers做全量赋值,减少不必要的重渲染,也避免状态更新时序问题
  • 给effect增加卸载标记,防止组件卸载后还执行状态更新造成内存泄漏,适配严格模式的校验逻辑
  • 直接用新拉取的全量数据覆盖旧状态,不要做追加操作,从逻辑上避免重复数据

修复后的完整代码如下:

useEffect(() => {
  // 卸载标记,阻止组件卸载后的状态更新
  let isUnmounted = false

  const SelectData = async () => {
    try {
      const snapshot = await get(child(dbref, 'GamePot Users/'))
      // 组件已卸载就不再执行后续逻辑
      if (isUnmounted) return
      if (!snapshot.exists()) {
        setUsers([])
        return
      }
      // 统一处理所有数据
      const fetchedUsers = []
      snapshot.forEach((doc) => {
        const key = doc.key
        const childData = doc.val()
        const parsedChildData = JSON.parse(childData)
        fetchedUsers.push({
          id: key,
          value: parsedChildData
        })
      })
      // 单次赋值覆盖,从根源避免重复追加
      setUsers(fetchedUsers)
    } catch (error) {
      console.error('数据库拉取失败:', error)
    }
  }

  SelectData()

  // 清理函数,effect重跑/组件卸载前触发
  return () => {
    isUnmounted = true
  }
}, [])

不推荐通过移除入口文件的<React.StrictMode>标签来规避双调用问题。严格模式的双执行是React官方提供的开发调试能力,只要你正确编写了副作用清理逻辑,开发环境的双调用不会产生业务bug,还能帮你提前发现潜在的内存泄漏、逻辑重复问题。

内容的提问来源于stack exchange,提问作者zanthez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 21:09:18