React Strict Mode下useReducer触发重复结果问题排查
Strict Mode下useReducer重复添加元素的解决方案
问题根源
你的reducer函数存在可变操作:虽然用{ ...state }浅拷贝了state对象,但newState.elements还是原数组的引用,调用push会直接修改原数组。而React Strict Mode在开发环境中会重复执行reducer(这是预期行为,用于检测不纯代码),第一次执行时原数组已经被修改,第二次执行push就会再添加一个元素,最终导致每次点击新增两个时间戳。
正确的reducer写法
必须保证reducer是纯函数,永远不要修改原state,而是返回全新的state对象和数组:
const reducer = (state, action) => { if (action.type === 'addElement') { // 生成新的elements数组,不修改原数组 return { ...state, elements: [ ...state.elements, { id: (new Date()).toString() } ] }; } // 一定要处理默认情况,返回原state return state; };
额外优化点
初始state中的new Date()在Strict Mode下也会被执行两次,导致初始渲染的时间戳其实是两个不同的值(虽然页面只显示一个,但不符合预期)。可以将初始state改为函数形式,确保只执行一次:
const getInitialState = () => ({ elements: [ { id: (new Date()).toString() } ] }); // 使用时传入函数 const [state, dispatch] = useReducer(reducer, getInitialState());
完整修正后的代码
import { useReducer } from 'react'; const reducer = (state, action) => { if (action.type === 'addElement') { return { ...state, elements: [ ...state.elements, { id: (new Date()).toString() } ] }; } return state; }; const getInitialState = () => ({ elements: [ { id: (new Date()).toString() } ] }); export default function Test() { const [state, dispatch] = useReducer(reducer, getInitialState()); function addElement() { dispatch({ type: 'addElement' }); } return ( <> <h2>Elements</h2> {/* 直接遍历elements数组,简化写法 */} {state.elements.map((item) => ( <div key={item.id}> <span>{item.id}</span><br /> </div> ))} <button type='button' onClick={addElement}>Add Element</button> </> ); }
关键注意事项
- reducer必须是纯函数:相同输入必须返回相同输出,不能修改入参,不能有副作用(开发环境Strict Mode的重复执行仅用于检测,生产环境不会触发)。
- 处理数组/对象时,要创建新的引用,不要直接修改原数据,比如用扩展运算符
[...arr]、数组方法(如filter、map)生成新数组。
内容的提问来源于stack exchange,提问作者Chris Crawshaw
相关产品推荐
相关产品推荐

