为何传入的依赖已变化,useMemo却未重新执行?
问题解答
你的理解是否正确?
你对useMemo的核心逻辑理解是对的:它会缓存回调函数的计算结果,仅当依赖项数组中的值发生引用变化时,才会重新执行回调并更新缓存结果。
你的实现方式存在的问题
- 冗余的状态存储:useMemo本身已经可以缓存过滤后的结果,没必要再将其存入useState——这会多一次状态更新,可能触发额外的组件重渲染,完全是画蛇添足。
- 依赖项无法检测到内部变化:你的TaskStore用Mobx管理状态,当通过
setCompleted修改数组中任务的completed属性时,数组本身的引用并没有改变(你只是修改了数组内对象的属性,没有替换整个数组)。而React的useMemo依赖项对比的是引用值,所以它会认为TaskStore.tasks没有变化,不会重新执行过滤逻辑,导致incompleteTasks始终是旧值。
正确的实现方式
结合Mobx的特性,推荐两种更合理的写法:
方式1:在Store中定义计算属性
直接在TaskStore里用Mobx的computed定义过滤后的任务列表,组件中直接使用这个计算属性,Mobx会自动追踪依赖并在数据变化时更新:
import { makeAutoObservable, computed } from "mobx"; class TaskStore { public tasks: Task[] = []; constructor() { makeAutoObservable(this); } @action setCompleted = (task: Task, completed: number) => { const index = this.tasks.indexOf(task); if (index !== -1) { this.tasks[index].completed = !!completed; } }; // 定义计算属性,自动过滤未完成任务 get incompleteTasks() { return this.tasks.filter(task => !task.completed); } }
组件中用observer包裹,直接使用计算属性:
import { observer } from "mobx-react-lite"; const TaskList = observer(() => { const { incompleteTasks } = TaskStore; return ( <div> {/* 渲染未完成任务列表 */} {incompleteTasks.map(task => ( <div key={task.id}>{task.name}</div> ))} </div> ); });
方式2:在observer组件内直接过滤
如果不想在Store里加计算属性,也可以直接在组件内过滤,只要用observer包裹组件,Mobx会追踪tasks数组内的属性变化,自动触发重渲染和过滤:
import { observer } from "mobx-react-lite"; const TaskList = observer(() => { const incompleteTasks = TaskStore.tasks.filter(task => !task.completed); return ( <div> {incompleteTasks.map(task => ( <div key={task.id}>{task.name}</div> ))} </div> ); });
总结
- useMemo的基础理解没问题,但你的实现不符合Mobx的最佳实践,且存在依赖检测失效的问题。
- 结合Mobx时,优先用
observer包裹组件+Store计算属性/组件内直接过滤的方式,让Mobx负责状态追踪和重渲染触发,避免手动用useMemo+useState的冗余写法。
内容的提问来源于stack exchange,提问作者Chan Kim
相关产品推荐
相关产品推荐

