乐闻世界logo
搜索文章和话题

MobX 常见问题和解决方案有哪些?

2月21日 15:50

MobX 是一个功能强大的状态管理库,但在使用过程中可能会遇到一些常见问题。了解这些问题及其解决方案可以帮助开发者更好地使用 MobX。

1. 组件不更新

问题描述

组件使用 observer 包装,但状态变化时组件不更新。

常见原因和解决方案

原因 1:访问的是普通对象而不是 observable

javascript
// 错误 const store = { count: 0 }; @observer class Counter extends React.Component { render() { return <div>{store.count}</div>; // 不会更新 } } // 正确 import { observable } from 'mobx'; const store = observable({ count: 0 }); @observer class Counter extends React.Component { render() { return <div>{store.count}</div>; // 会更新 } }

原因 2:在 action 外修改状态(MobX 6)

javascript
// 错误(MobX 6) class Store { @observable count = 0; increment() { this.count++; // 不在 action 中,会报错 } } // 正确 class Store { @observable count = 0; @action increment() { this.count++; // 在 action 中 } }

原因 3:在 render 中创建新对象

javascript
// 错误 @observer class Component extends React.Component { render() { const style = { color: 'red' }; // 每次渲染都创建新对象 return <div style={style}>{store.count}</div>; } } // 正确 const style = { color: 'red' }; // 在组件外部定义 @observer class Component extends React.Component { render() { return <div style={style}>{store.count}</div>; } }

2. 性能问题

问题描述

应用性能下降,组件频繁重新渲染。

常见原因和解决方案

原因 1:过度追踪

javascript
// 错误:在循环中访问 observable @observer class List extends React.Component { render() { return ( <div> {store.items.map(item => ( <div key={item.id}> {item.name} - {item.value} - {item.description} </div> ))} </div> ); } } // 正确:使用 computed 预处理数据 class Store { @observable items = []; @computed get itemDisplayData() { return this.items.map(item => ({ id: item.id, display: `${item.name} - ${item.value} - ${item.description}` })); } } @observer class List extends React.Component { render() { return ( <div> {store.itemDisplayData.map(item => ( <div key={item.id}>{item.display}</div> ))} </div> ); } }

原因 2:组件依赖太多状态

javascript
// 错误:组件依赖太多状态 @observer class Dashboard extends React.Component { render() { return ( <div> <UserInfo /> <Settings /> <DataCount /> </div> ); } } // 正确:拆分为多个组件 @observer class UserInfo extends React.Component { render() { return ( <div> <div>{store.user.name}</div> <div>{store.user.email}</div> </div> ); } }

3. 内存泄漏

问题描述

组件卸载后,reaction 仍然在运行,导致内存泄漏。

解决方案

javascript
// 错误:忘记清理 reaction useEffect(() => { autorun(() => { console.log(store.count); }); }, []); // 正确:清理 reaction useEffect(() => { const dispose = autorun(() => { console.log(store.count); }); return () => { dispose(); // 清理 reaction }; }, []);

4. 异步操作问题

问题描述

异步操作中的状态修改不生效。

解决方案

javascript
// 错误:异步操作中的状态修改 @action async fetchData() { this.loading = true; const data = await fetch('/api/data').then(r => r.json()); this.data = data; // 不在 action 中 this.loading = false; // 不在 action 中 } // 正确:使用 runInAction @action async fetchData() { this.loading = true; try { const data = await fetch('/api/data').then(r => r.json()); runInAction(() => { this.data = data; }); } finally { runInAction(() => { this.loading = false; }); } } // 或者使用 flow fetchData = flow(function* () { this.loading = true; try { const response = yield fetch('/api/data'); const data = yield response.json(); this.data = data; } finally { this.loading = false; } });

5. computed 不更新

问题描述

computed 属性没有按预期更新。

常见原因和解决方案

原因 1:在 computed 中产生副作用

javascript
// 错误:在 computed 中产生副作用 @computed get badComputed() { console.log('Side effect!'); // 不应该在 computed 中 fetch('/api/data'); // 不应该在 computed 中 return this.data; } // 正确:computed 应该是纯函数 @computed get goodComputed() { return this.data.filter(item => item.active); }

原因 2:依赖项没有正确追踪

javascript
// 错误:依赖项没有正确追踪 @computed get badComputed() { const data = this.data; // 没有在返回值中使用 return this.items.length; } // 正确:依赖项正确追踪 @computed get goodComputed() { return this.data.length + this.items.length; }

6. 循环依赖

问题描述

多个 store 之间存在循环依赖,导致无限循环或性能问题。

解决方案

javascript
// 错误:循环依赖 class StoreA { @observable value = 0; @computed get doubled() { return storeB.value * 2; } } class StoreB { @observable value = 0; @computed get doubled() { return storeA.value * 2; } } // 正确:避免循环依赖 class Store { @observable valueA = 0; @observable valueB = 0; @computed get doubledA() { return this.valueA * 2; } @computed get doubledB() { return this.valueB * 2; } }

7. 装饰器不工作

问题描述

使用装饰器时出现错误或不生效。

解决方案

确保配置正确

javascript
// package.json { "babel": { "plugins": [ ["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/plugin-proposal-class-properties", { "loose": true }] ] } }

或者使用 makeObservable

javascript
// 不使用装饰器 class Store { count = 0; constructor() { makeObservable(this, { count: observable }); } }

8. TypeScript 类型错误

问题描述

使用 TypeScript 时出现类型错误。

解决方案

javascript
// 错误:没有类型参数 class Store { count = 0; constructor() { makeObservable(this, { count: observable }); } } // 正确:使用类型参数 class Store { count: number = 0; constructor() { makeObservable<Store>(this, { count: observable }); } }

9. 数组操作问题

问题描述

数组操作不触发更新。

解决方案

javascript
// 错误:重新赋值整个数组 @action badAddItem(item) { this.items = [...this.items, item]; } // 正确:使用数组方法 @action goodAddItem(item) { this.items.push(item); } // 或者使用 replace @action replaceItems(newItems) { this.items.replace(newItems); }

10. 调试困难

问题描述

难以追踪状态变化和依赖关系。

解决方案

使用 trace

javascript
import { trace } from 'mobx'; // 追踪 computed trace(store.fullName); // 追踪组件渲染 @observer class MyComponent extends React.Component { render() { trace(true); // 追踪组件渲染 return <div>{store.count}</div>; } }

使用 MobX DevTools

javascript
import { configure } from 'mobx'; configure({ // 启用调试模式 useProxies: 'ifavailable', isolateGlobalState: true });

最佳实践总结

  1. 始终在 action 中修改状态(MobX 6)
  2. 使用 observer 包装需要响应状态变化的组件
  3. 避免在 render 中创建新对象
  4. 使用 computed 优化计算逻辑
  5. 及时清理 reaction 和副作用
  6. 正确处理异步操作
  7. 避免循环依赖
  8. 使用 trace 和 DevTools 调试
  9. 合理拆分组件以减少依赖
  10. 遵循 MobX 的最佳实践

遵循这些最佳实践,可以避免大多数常见的 MobX 问题,构建稳定、高效的应用。

标签:Mobx