Redux todo应用中remainingTasks/updateRemainingTasks action触发两次问题咨询
这是代码写法问题导致的异常现象,而非正常逻辑。
根因分析
你直接在RemainingTasks组件的顶层执行dispatch(updateRemainingTasks(incompleteTasks)),而React组件每次渲染时都会执行函数体内所有顶层代码,因此会形成如下触发循环:
- 你执行添加/切换任务完成状态/删除未完成任务操作时,首先会修改
taskSlice的状态 RemainingTasks组件订阅了taskSlice,因此触发第一次渲染,执行顶层的dispatch,更新remainingTasks状态- 组件同时订阅了
remainingTasks状态,状态变更后触发第二次渲染,再次执行dispatch,若此时计算出的incompleteTasks和上一次payload相同,Redux状态无更新,循环终止,因此你会看到两次action触发 - 当你删除已完成任务时,计算出的
incompleteTasks和原有remainingTasks值完全一致,第一次dispatch后Redux状态没有变化,不会触发第二次渲染,因此仅能看到一次action触发
优化方案
最优方案:移除冗余的remainingTasks状态
剩余待办数完全是从任务列表派生出来的状态,没有必要单独存储,直接删除对应的slice,组件里直接使用计算出的incompleteTasks渲染即可,无需任何dispatch操作,修改后的组件代码如下:
import { useSelector } from "react-redux"; import { selectTaskSlice } from "../task/taskSlice"; const RemainingTasks = () => { const taskSlice = useSelector(selectTaskSlice); // 直接计算未完成任务数渲染即可 const incompleteTasks = taskSlice.filter((task) => !task.completed).length; return ( <div> <h1 className="header"> {incompleteTasks > 1 ? `${incompleteTasks} Tasks Left` : `${incompleteTasks} Task Left`} </h1> </div> ); }; export default RemainingTasks;
兼容方案:将dispatch放到副作用中执行
如果你因特殊需求必须保留独立的remainingTasks状态,需要把dispatch操作放到useEffect中,仅当未完成任务数变化时才触发更新,避免每次渲染都执行dispatch:
import { useSelector, useDispatch } from "react-redux"; import { useEffect } from "react"; import { selectRemainingTasksSlice, updateRemainingTasks, } from "./remainingTasksSlice"; import { selectTaskSlice } from "../task/taskSlice"; const RemainingTasks = () => { const dispatch = useDispatch(); const remainingTasksSlice = useSelector(selectRemainingTasksSlice); const taskSlice = useSelector(selectTaskSlice); const incompleteTasks = taskSlice.filter((task) => !task.completed).length; // 仅当incompleteTasks变化时才更新状态 useEffect(() => { dispatch(updateRemainingTasks(incompleteTasks)); }, [dispatch, incompleteTasks]) return ( <div> <h1 className="header"> {remainingTasksSlice > 1 ? `${remainingTasksSlice} Tasks Left` : `${remainingTasksSlice} Task Left`} </h1> </div> ); }; export default RemainingTasks;
内容的提问来源于stack exchange,提问作者Nima Zarei
相关产品推荐
相关产品推荐

