React中useEffect setState后console.log多次输出原因咨询
问题描述
我通过axios的fetchData('/content')从MongoDB获取数据,将数据存入新数组后调用setTexts更新state。功能正常实现,但console.log(texts)会输出两次(React严格模式下四次);若不使用forEach,仅输出一次(严格模式下两次)。尝试用map替代forEach、调整代码位置均无效,作为React新手,想了解该现象的原因。
相关代码:
const [texts, setTexts] = useState('') useEffect(() => { const getText = async () => { const datas = await fetchData("/content") const values = [] datas.data.forEach(text => { values[text.name] = text.tex_fr || text.items }); setTexts(values) } getText() }, []) console.log(texts)
原因解析
首先明确:React组件每渲染一次,就会执行一次console.log(texts),所以输出次数等于组件渲染次数。
核心问题:用数组存储非索引键的属性
你初始化values为数组[],但通过values[text.name] = ...给它添加字符串类型的键——JS里允许这么做,但数组的设计初衷是存储按索引排列的有序数据,你这种用法本质是把数组当成普通对象在用。
当调用setTexts(values)时,React会做浅比较来判断是否需要更新组件:
- 若不用
forEach,直接给values赋值普通对象/正常数组,React能准确识别状态变化,触发一次更新。 - 但你用数组存非索引键时,数组的
length属性没变化(你没通过push等方法添加元素),React第一次浅比较可能认为状态没变化;但你确实修改了数组的属性,后续又会判定状态变化,导致额外的一次渲染,最终输出两次。
严格模式的放大作用
React严格模式会重复执行渲染、副作用逻辑(比如useEffect里的代码会跑两次),目的是提前暴露潜在问题。所以正常模式下两次输出,严格模式下会变成四次,这是严格模式的正常行为。
修复方案
把values的初始化改成对象{},因为你用字符串作为键存储数据,对象才是匹配的数据结构:
const [texts, setTexts] = useState({}) // 同时把初始state从''改成{}更合理 useEffect(() => { const getText = async () => { const datas = await fetchData("/content") const values = {} // 从[]改为{} datas.data.forEach(text => { values[text.name] = text.tex_fr || text.items }); setTexts(values) } getText() }, []) console.log(texts)
修改后React能正确识别状态变化,渲染次数会回到正常的两次(严格模式下四次,属于预期行为)。
内容的提问来源于stack exchange,提问作者Manon Bruimaud
相关产品推荐
相关产品推荐

