Redux Store无法触发React函数组件重渲染问题求助
问题描述
使用React函数组件时遇到Redux Store更新后组件不重渲染的问题。Redux DevTools显示状态流转正常且已正确更新状态,但App组件未按预期重渲染。以下是相关代码:
App.js
import Habbit from "./Habbit"; import NavBar from "./NavBar"; import HabitTrackerWeekView from "./HabitTrackerWeekView"; import { switchComponents, addHabit } from "../Action"; import { useEffect, useState } from "react"; function App({ store }) { const [newHabit, setNewHabit] = useState(""); const { habitList, switchCom, currentMonth, doneCount } = store.getState(); useEffect(() => { console.log("app.js done count ", doneCount); }, [doneCount]); return ( <div className="App"> <NavBar /> <HabitTrackerWeekView habits={store.getState()} dispatch={store.dispatch} currentMonth={currentMonth} /> </div> ); } export default App;
HabitTrackerWeekView.js
import React, { useEffect, useReducer, useState } from "react"; import { countDoneStatuses } from "../Action"; import { format, subWeeks, startOfWeek, endOfWeek, eachDayOfInterval, addWeeks, } from "date-fns"; function HabitTrackerWeekView({ habits, dispatch }) { const { habitList } = habits; const [currentMonth, setCurrentMonth] = useState(new Date()); const [habitStatuses, setHabitStatuses] = useState({}); const [habitDoneCounts, setHabitDoneCounts] = useState( habitList.reduce((counts, habit) => ({ ...counts, [habit]: 0 }), {}) ); useEffect(() => { // console.log("Current habitDoneCounts:", habitDoneCounts); dispatch(countDoneStatuses(habitDoneCounts)); }, [habitDoneCounts]); const weekdays = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", ]; const getCurrentWeekDates = () => { const startOfCurrentWeek = startOfWeek(currentMonth, { weekStartsOn: 1 }); const endOfCurrentWeek = endOfWeek(currentMonth, { weekStartsOn: 1 }); const datesOfCurrentWeek = eachDayOfInterval({ start: startOfCurrentWeek, end: endOfCurrentWeek, }); const formattedDatesOfCurrentWeek = datesOfCurrentWeek.map((date) => format(date, "dd") ); return formattedDatesOfCurrentWeek; }; const dates = getCurrentWeekDates(); const handlePreviousWeek = () => { setCurrentMonth(subWeeks(currentMonth, 1)); }; const getMonthYear = () => { const dateFormat = "MMM yyyy"; return format(currentMonth, dateFormat); }; const handleNextWeek = () => { setCurrentMonth(addWeeks(currentMonth, 1)); }; const handleTodaysStatus = (date, habit) => { const key = `${date}-${habit}`; setHabitStatuses((prevStatuses) => { const currentStatus = prevStatuses[key] || "none"; const newStatus = currentStatus === "none" ? "done" : currentStatus === "done" ? "not done" : "none"; setHabitDoneCounts((prevCounts) => ({ ...prevCounts, [habit]: newStatus === "done" ? prevCounts[habit] + 1 : Math.max(0, prevCounts[habit] - 1), })); return { ...prevStatuses, [key]: newStatus }; }); // console.log(habitStatuses); }; return ( <div className="habit-tracker-week-view"> <div className="months"> <div className="previous-month" onClick={handlePreviousWeek}> <img width="24" height="24" src="https://img.icons8.com/material-outlined/24/back--v1.png" alt="back--v1" /> </div> <div className="month">{getMonthYear()}</div> <div className="next-month" onClick={handleNextWeek}> <img width="24" height="24" src="https://img.icons8.com/material-outlined/24/forward.png" alt="forward" /> </div> </div> <div className="day-of-the-week"> {weekdays.map((day) => ( <label>{day}</label> ))} </div> {/* <h1>Habit Tracker</h1> */} <div className="habits"> {habitList.map((habit) => ( <> <div className="habit-info"> <div className="habit-name">{habit}</div> <div className="habit-time">time</div> </div> <div className="habit-dates"> {dates.map((date) => ( <div className={`habit-date ${ habitStatuses[`${date}-${habit}`] === "done" ? "done" : `${ habitStatuses[`${date}-${habit}`] === "not done" ? "not-done" : "" }` }`} onClick={() => handleTodaysStatus(date, habit)} > {date} </div> ))} </div> </> ))} </div> </div> ); } export default HabitTrackerWeekView;
解决方案
核心问题是App组件没有订阅Redux Store的状态变化。直接调用store.getState()只会在组件初始渲染时获取一次状态,后续Store更新时不会自动触发组件重渲染。
方法1:使用react-redux的useSelector和useDispatch(推荐)
这是React Redux官方推荐的方式,会自动订阅状态变化,当选中的状态更新时触发组件重渲染。
修改App.js:
import Habbit from "./Habbit"; import NavBar from "./NavBar"; import HabitTrackerWeekView from "./HabitTrackerWeekView"; import { switchComponents, addHabit } from "../Action"; import { useEffect, useState } from "react"; // 导入useSelector和useDispatch import { useSelector, useDispatch } from "react-redux"; function App() { // 不再需要传入store const [newHabit, setNewHabit] = useState(""); // 使用useSelector获取状态 const { habitList, switchCom, currentMonth, doneCount } = useSelector(state => state); const dispatch = useDispatch(); // 获取dispatch函数 useEffect(() => { console.log("app.js done count ", doneCount); }, [doneCount]); return ( <div className="App"> <NavBar /> <HabitTrackerWeekView habits={useSelector(state => state)} // 同样用useSelector传递状态 dispatch={dispatch} currentMonth={currentMonth} /> </div> ); } export default App;
同时确保根组件已用Provider包裹(示例index.js):
import { Provider } from 'react-redux'; import store from './store'; // 你的Redux store配置 import App from './App'; ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );
方法2:手动订阅Store变化(不推荐,仅作了解)
若不想使用react-redux hooks,可手动订阅store的subscribe方法,状态变化时强制组件更新:
修改App.js:
import Habbit from "./Habbit"; import NavBar from "./NavBar"; import HabitTrackerWeekView from "./HabitTrackerWeekView"; import { switchComponents, addHabit } from "../Action"; import { useEffect, useState } from "react"; function App({ store }) { const [newHabit, setNewHabit] = useState(""); // 用state保存当前状态,触发重渲染 const [state, setState] = useState(store.getState()); useEffect(() => { // 订阅store变化,每次更新时重新获取状态 const unsubscribe = store.subscribe(() => { setState(store.getState()); }); // 组件卸载时取消订阅 return () => unsubscribe(); }, [store]); const { habitList, switchCom, currentMonth, doneCount } = state; useEffect(() => { console.log("app.js done count ", doneCount); }, [doneCount]); return ( <div className="App"> <NavBar /> <HabitTrackerWeekView habits={state} dispatch={store.dispatch} currentMonth={currentMonth} /> </div> ); } export default App;
额外注意点
- 传递
habits时,建议仅传递组件需要的字段(如habitList),避免不必要的重渲染。 - 确保Redux reducer是纯函数,返回新的状态对象/数组而非修改原有状态。若reducer直接修改原状态,即使DevTools显示更新,组件也可能不触发重渲染。
内容的提问来源于stack exchange,提问作者RAHUL pathak
相关产品推荐
相关产品推荐

