Akita状态更新后组件为什么不重新渲染?

技术艺童 阅读 18

我用Akita做状态管理,修改store里的数据后,React组件居然没重新渲染,是不是哪里写错了?

我试过用update方法直接改对象属性,也试过用produce,但都没用。控制台打印新状态是对的,就是界面不动。

const { todos } = useTodosStore();
const addTodo = () => {
  todosStore.update(state => ({
    ...state,
    list: [...state.list, { id: Date.now(), text: 'new' }]
  }));
};
// 组件里就直接用了 {todos.list.map(...)}
我来解答 赞 3 收藏
二维码
手机扫码查看
1 条解答
司马子源
你这个写法其实没大问题,问题大概率出在 Akita 的 EntityStore 用法上——你是不是用的是 EntityStore 而不是 Store?如果是 EntityStore,它默认是按 entity id 做变更检测的,直接 list: [...state.list, newTodo] 这种数组替换不会触发 React 重渲染,因为 Akita 内部比较的是 entities 的 id 集合,不是整个数组引用。

先确认下你的 store 定义:
如果是这种:

class TodosStore extends EntityStore(TodosState) {
constructor() {
super();
}
}


那它内部会把 list 当作 entities 的索引容器,实际数据存在 entities 对象里。这时候你得用 addEntitiesupdateEntities

todosStore.addEntities({ [Date.now()]: { id: Date.now(), text: 'new' } });


或者如果你坚持要用数组形式存 list,那就别用 EntityStore,直接用普通 Store

const store = new Store({ list: [] });


另外,produce 也能用,但得确保你返回的是新对象而不是改原对象,比如:

todosStore.update(
produce(state => {
state.list.push({ id: Date.now(), text: 'new' });
})
);


不过要注意,produce 是 Immer 的 API,要确认你引入的是 @datorama/akitaproduce 还是自己手动引入的,有些版本里 update 不配合 Immer 的 produce 直接用会失效。

最后别忘了组件里订阅的时候是不是这样写的:

const { list } = useTodosStore(state => ({ list: state.list }));


如果只是 const { todos } = useTodosStore() 然后 {todos.list},那 todos 对象本身引用没变,React 就不会重渲染——必须解构出具体的 list 才行,这点特别容易踩坑。

总结下排查顺序:
1. 检查 store 类型(EntityStore 还是普通 Store)
2. 看组件订阅方式是不是解构了具体字段
3. 更新方式是否符合 store 类型的规范
4. produce 的使用是否正确

我之前也卡在这过,Akita 文档没写清楚这个坑,EntityStore 和普通 Store 的行为差异太大了,稍微一不小心就白调一小时 API。
点赞 2
2026-02-26 19:06