useReducer dispatch触发两次,移除StrictMode后UI不更新求助
问题描述
我写了一段React代码,原本useReducer的dispatch方法会触发两次。查资料后移除了index.js里的React.StrictMode,dispatch现在能正常执行一次,但UI不会渲染更新后的slots值,求解决办法。
相关代码
Reservations组件代码
import React, { useReducer, useState } from "react"; import "../App.css"; const initialState = [ { time: "17:00", slots: 3 }, { time: "18:00", slots: 4 }, { time: "19:00", slots: 2 }, { time: "20:00", slots: 4 }, { time: "21:00", slots: 3 }, { time: "22:00", slots: 2 }, ]; const reducer = (state, action) => { switch (action.type) { case "update": { let index = state.findIndex((x) => x.time === action.value); console.log(index); if (state[index].slots > 0) { state[index].slots -= 1; } console.log(state); return state; } } }; function Reservations() { const [availableTimes, dispatch] = useReducer(reducer, initialState); const [time, setTime] = useState("17:00"); // 补充原代码缺失的状态声明 const handleSubmit = (e) => { e.preventDefault(); dispatch({ type: "update", value: time }); }; return ( <> <label htmlFor="res-time">选择时间</label> <select id="res-time" value={time} onChange={(e) => setTime(e.target.value)} > {availableTimes.map((availableTime) => { return ( <option key={availableTime.time} value={availableTime.time}> {availableTime.time} - 剩余{availableTime.slots}个预约位 </option> ); })} </select> <form onSubmit={handleSubmit}> <button type="submit">确认预约</button> </form> </> ); } export default Reservations;
index.js代码
import React from "react"; import ReactDOM from "react-dom/client"; import "./index.css"; import App from "./App"; import reportWebVitals from "./reportWebVitals"; import { BrowserRouter } from "react-router-dom"; import "bootstrap/dist/css/bootstrap.min.css"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( // <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> // </React.StrictMode> );
解决方案
核心问题是你在reducer里直接修改了原state的引用,React依赖状态的引用变化来判断是否需要重新渲染组件。必须返回全新的状态对象/数组,不能直接修改原state。
修改reducer函数如下:
const reducer = (state, action) => { switch (action.type) { case "update": { return state.map(item => { if (item.time === action.value && item.slots > 0) { // 返回新对象,不修改原数据 return { ...item, slots: item.slots - 1 }; } // 未修改的项直接返回原对象 return item; }); } // 处理默认情况,避免返回undefined default: return state; } };
关键说明:
- 使用
map创建新数组,替代直接修改原数组的操作 - 对需要更新的项,用扩展运算符
...复制原对象的所有属性,再修改slots值,保证返回新引用 - 添加
default分支,确保任何未知action类型都能返回原state,避免潜在报错 - 原代码缺失
time状态的声明,已补充完整,否则select组件会出现运行错误
内容的提问来源于stack exchange,提问作者Philomath
相关产品推荐
相关产品推荐

