React useEffect空依赖执行两次 实时数据库数据重复打印
问题原因
两个因素共同导致你看到的重复打印、数据重复问题:
- React 18+ 开发环境默认开启严格模式:对空依赖的
useEffect,严格模式会在开发阶段主动执行「挂载 -> 模拟卸载 -> 重新挂载」的双调用流程,目的是提前暴露没有正确清理副作用的代码问题,这个行为仅在开发环境存在,生产环境不会触发。 - 你的状态更新逻辑本身有缺陷:你在遍历数据库返回的快照时,循环调用
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
相关产品推荐
相关产品推荐

