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

如何在 React 中使用 MobX?

2月22日 14:05

在 React 中使用 MobX 需要将 MobX 的响应式状态与 React 的渲染机制连接起来。MobX 提供了多种方式来实现这种集成。

安装依赖

bash
npm install mobx mobx-react-lite # 或者 npm install mobx mobx-react

使用方式

1. 使用 observer 高阶组件(mobx-react)

javascript
import React from 'react'; import { observer } from 'mobx-react'; import { makeAutoObservable } from 'mobx'; class TodoStore { todos = []; constructor() { makeAutoObservable(this); } addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }); } toggleTodo(id) { const todo = this.todos.find(t => t.id === id); if (todo) todo.completed = !todo.completed; } } const todoStore = new TodoStore(); // 使用 observer 包装组件 const TodoList = observer(() => { return ( <div> <h1>Todo List</h1> <ul> {todoStore.todos.map(todo => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => todoStore.toggleTodo(todo.id)} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> </li> ))} </ul> <button onClick={() => todoStore.addTodo('New Todo')}> Add Todo </button> </div> ); }); export default TodoList;

2. 使用 useObserver Hook(mobx-react-lite)

javascript
import React from 'react'; import { useObserver } from 'mobx-react-lite'; import { makeAutoObservable } from 'mobx'; class TodoStore { todos = []; constructor() { makeAutoObservable(this); } addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }); } toggleTodo(id) { const todo = this.todos.find(t => t.id === id); if (todo) todo.completed = !todo.completed; } } const todoStore = new TodoStore(); function TodoList() { return useObserver(() => ( <div> <h1>Todo List</h1> <ul> {todoStore.todos.map(todo => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => todoStore.toggleTodo(todo.id)} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> </li> ))} </ul> <button onClick={() => todoStore.addTodo('New Todo')}> Add Todo </button> </div> )); } export default TodoList;

3. 使用 useLocalObservable Hook(mobx-react-lite)

javascript
import React from 'react'; import { observer, useLocalObservable } from 'mobx-react-lite'; function TodoList() { const store = useLocalObservable(() => ({ todos: [], addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }); }, toggleTodo(id) { const todo = this.todos.find(t => t.id === id); if (todo) todo.completed = !todo.completed; } })); return ( <div> <h1>Todo List</h1> <ul> {store.todos.map(todo => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => store.toggleTodo(todo.id)} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> </li> ))} </ul> <button onClick={() => store.addTodo('New Todo')}> Add Todo </button> </div> ); } export default observer(TodoList);

4. 使用 React Context 共享 Store

javascript
import React, { createContext, useContext } from 'react'; import { observer } from 'mobx-react'; import { makeAutoObservable } from 'mobx'; class TodoStore { todos = []; constructor() { makeAutoObservable(this); } addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }); } toggleTodo(id) { const todo = this.todos.find(t => t.id === id); if (todo) todo.completed = !todo.completed; } } const TodoContext = createContext(null); function TodoProvider({ children }) { const store = new TodoStore(); return ( <TodoContext.Provider value={store}> {children} </TodoContext.Provider> ); } function useTodoStore() { const store = useContext(TodoContext); if (!store) { throw new Error('useTodoStore must be used within TodoProvider'); } return store; } const TodoList = observer(() => { const store = useTodoStore(); return ( <div> <h1>Todo List</h1> <ul> {store.todos.map(todo => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => store.toggleTodo(todo.id)} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> </li> ))} </ul> <button onClick={() => store.addTodo('New Todo')}> Add Todo </button> </div> ); }); function App() { return ( <TodoProvider> <TodoList /> </TodoProvider> ); } export default App;

5. 使用 Provider 和 inject(mobx-react 旧版)

javascript
import React from 'react'; import { Provider, observer, inject } from 'mobx-react'; import { makeAutoObservable } from 'mobx'; class TodoStore { todos = []; constructor() { makeAutoObservable(this); } addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }); } toggleTodo(id) { const todo = this.todos.find(t => t.id === id); if (todo) todo.completed = !todo.completed; } } const todoStore = new TodoStore(); @inject('todoStore') @observer class TodoList extends React.Component { render() { const { todoStore } = this.props; return ( <div> <h1>Todo List</h1> <ul> {todoStore.todos.map(todo => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => todoStore.toggleTodo(todo.id)} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> </li> ))} </ul> <button onClick={() => todoStore.addTodo('New Todo')}> Add Todo </button> </div> ); } } function App() { return ( <Provider todoStore={todoStore}> <TodoList /> </Provider> ); } export default App;

最佳实践

1. 使用 observer 包裹需要响应式更新的组件

javascript
// ✅ 正确:只包裹需要响应式更新的组件 const TodoItem = observer(({ todo }) => ( <li> <input type="checkbox" checked={todo.completed} onChange={() => todo.toggle()} /> <span>{todo.text}</span> </li> )); const TodoList = ({ store }) => ( <ul> {store.todos.map(todo => ( <TodoItem key={todo.id} todo={todo} /> ))} </ul> ); // ❌ 错误:包裹整个应用,可能导致不必要的渲染 const App = observer(() => ( <div> <Header /> <TodoList /> <Footer /> </div> ));

2. 合理拆分 Store

javascript
// ✅ 正确:按功能拆分 Store class TodoStore { @observable todos = []; @observable filter = 'all'; @action addTodo(text) { this.todos.push({ id: Date.now(), text, completed: false }); } } class UserStore { @observable user = null; @action setUser(user) { this.user = user; } } class AppStore { todoStore = new TodoStore(); userStore = new UserStore(); } // 使用 Context 共享 const StoreContext = createContext(new AppStore());

3. 使用 computed 优化性能

javascript
class TodoStore { @observable todos = []; @observable filter = 'all'; @computed get filteredTodos() { switch (this.filter) { case 'completed': return this.todos.filter(todo => todo.completed); case 'active': return this.todos.filter(todo => !todo.completed); default: return this.todos; } } } const TodoList = observer(({ store }) => ( <ul> {store.filteredTodos.map(todo => ( <TodoItem key={todo.id} todo={todo} /> ))} </ul> ));

4. 使用 React.memo 优化子组件

javascript
const TodoItem = observer(React.memo(({ todo }) => ( <li> <input type="checkbox" checked={todo.completed} onChange={() => todo.toggle()} /> <span>{todo.text}</span> </li> )));

5. 使用 useEffect 清理副作用

javascript
import { useEffect } from 'react'; import { reaction } from 'mobx'; const TodoList = observer(({ store }) => { useEffect(() => { const disposer = reaction( () => store.todos.length, (length) => { console.log('Todo count changed:', length); } ); return () => disposer(); }, [store]); return ( <ul> {store.todos.map(todo => ( <TodoItem key={todo.id} todo={todo} /> ))} </ul> ); });

常见问题

1. 组件不更新

javascript
// ❌ 错误:忘记使用 observer function TodoList({ store }) { return ( <ul> {store.todos.map(todo => ( <li key={todo.id}>{todo.text}</li> ))} </ul> ); } // ✅ 正确:使用 observer const TodoList = observer(({ store }) => ( <ul> {store.todos.map(todo => ( <li key={todo.id}>{todo.text}</li> ))} </ul> ));

2. 在 observer 组件外修改状态

javascript
// ❌ 错误:在 observer 组件外直接修改状态 function App() { const store = useStore(); useEffect(() => { store.todos.push({ id: 1, text: 'Todo' }); // 不在 action 中 }, []); return <TodoList store={store} />; } // ✅ 正确:在 action 中修改状态 function App() { const store = useStore(); useEffect(() => { store.addTodo('Todo'); // 在 action 中 }, []); return <TodoList store={store} />; }

3. 过度使用 observer

javascript
// ❌ 错误:过度使用 observer const Header = observer(() => <header>Header</header>); const Footer = observer(() => <footer>Footer</footer>); const Main = observer(() => <main>Main</main>); // ✅ 正确:只在需要响应式更新的组件使用 observer const Header = () => <header>Header</header>; const Footer = () => <footer>Footer</footer>; const Main = observer(() => <main>Main</main>);

总结

  • 使用 observer 或 useObserver 将组件连接到 MobX
  • 使用 React Context 共享 Store
  • 合理拆分 Store,按功能模块组织
  • 使用 computed 优化性能
  • 使用 React.memo 优化子组件
  • 记得在 useEffect 中清理 reaction
  • 避免过度使用 observer
  • 始终在 action 中修改状态
标签:Mobx