MobX 6 is the latest major version of MobX, with many important changes and improvements compared to MobX 4/5. Understanding these changes is important for upgrading and maintaining projects.
Major Changes
1. Removed Decorator Support
MobX 6 no longer supports decorator syntax by default, recommending the use of makeObservable or makeAutoObservable.
MobX 4/5 (Decorator Syntax):
javascriptimport { observable, action, computed } from 'mobx'; class TodoStore { @observable todos = []; @observable filter = 'all'; @computed get completedTodos() { return this.todos.filter(todo => todo.completed); } @action addTodo(text) { this.todos.push({ text, completed: false }); } }
MobX 6 (Recommended):
javascriptimport { makeAutoObservable } from 'mobx'; class TodoStore { todos = []; filter = 'all'; constructor() { makeAutoObservable(this); } get completedTodos() { return this.todos.filter(todo => todo.completed); } addTodo(text) { this.todos.push({ text, completed: false }); } }
2. makeObservable and makeAutoObservable
MobX 6 introduced two new APIs to replace decorators:
makeObservable
Need to explicitly specify the type of each property.
javascriptimport { makeObservable, observable, action, computed } from 'mobx'; class TodoStore { todos = []; filter = 'all'; constructor() { makeObservable(this, { todos: observable, filter: observable, completedTodos: computed, addTodo: action }); } get completedTodos() { return this.todos.filter(todo => todo.completed); } addTodo(text) { this.todos.push({ text, completed: false }); } }
makeAutoObservable (Recommended)
Automatically infer property types, more concise.
javascriptimport { makeAutoObservable } from 'mobx'; class TodoStore { todos = []; filter = 'all'; constructor() { makeAutoObservable(this); } get completedTodos() { return this.todos.filter(todo => todo.completed); } addTodo(text) { this.todos.push({ text, completed: false }); } }
3. Removed configure
MobX 6 removed the configure API, no longer requiring global configuration.
MobX 4/5:
javascriptimport { configure } from 'mobx'; configure({ enforceActions: 'always', useProxies: 'ifavailable' });
MobX 6:
javascript// No longer need configure, default behavior has been optimized
4. Removed extras
MobX 6 removed the extras API, related features have been integrated into the main API.
MobX 4/5:
javascriptimport { extras } from 'mobx'; const isObservable = extras.isObservable(obj);
MobX 6:
javascriptimport { isObservable } from 'mobx'; const isObservable = isObservable(obj);
5. Removed intercept and observe
MobX 6 removed the intercept and observe APIs, recommending the use of reaction instead.
MobX 4/5:
javascriptimport { observe } from 'mobx'; const disposer = observe(store.todos, (change) => { console.log('Todo changed:', change); });
MobX 6:
javascriptimport { reaction } from 'mobx'; const disposer = reaction( () => store.todos.length, (length) => { console.log('Todo count changed:', length); } );
6. Improved Type Inference
MobX 6 has better support for TypeScript with more accurate type inference.
typescriptimport { makeAutoObservable } from 'mobx'; class TodoStore { todos: Todo[] = []; filter: 'all' | 'completed' | 'active' = 'all'; constructor() { makeAutoObservable<TodoStore>(this); } get completedTodos(): Todo[] { return this.todos.filter(todo => todo.completed); } addTodo(text: string): void { this.todos.push({ text, completed: false }); } }
7. Removed inject and Provider from mobx-react
MobX 6 recommends using React Context API, no longer needing inject and Provider.
MobX 4/5:
javascriptimport { Provider, inject, observer } from 'mobx-react'; @inject('todoStore') @observer class TodoList extends React.Component { render() { const { todoStore } = this.props; return <div>{/* ... */}</div>; } } function App() { return ( <Provider todoStore={todoStore}> <TodoList /> </Provider> ); }
MobX 6:
javascriptimport { observer } from 'mobx-react-lite'; import { createContext, useContext } from 'react'; const TodoContext = createContext(null); function TodoProvider({ children, store }) { 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>{/* ... */}</div>; }); function App() { return ( <TodoProvider store={todoStore}> <TodoList /> </TodoProvider> ); }
8. Performance Improvements
MobX 6 has many performance improvements:
- Smaller bundle size: Reduced bundle size through Tree-shaking
- Faster response: Optimized dependency tracking algorithm
- Better memory management: Reduced memory usage
9. Improved Error Handling
MobX 6 provides clearer error messages.
javascript// MobX 6 provides clearer error messages class Store { data = []; constructor() { makeAutoObservable(this); } // If modifying state outside action modifyData() { this.data.push({}); // Warning: modifying state outside action } }
Migration Guide
Migrating from MobX 4/5 to MobX 6
1. Remove Decorators
Before:
javascriptclass Store { @observable data = []; @action addData(item) { this.data.push(item); } }
After:
javascriptclass Store { data = []; constructor() { makeAutoObservable(this); } addData(item) { this.data.push(item); } }
2. Update mobx-react
Before:
javascriptimport { Provider, inject, observer } from 'mobx-react'; @inject('store') @observer class Component extends React.Component { render() { const { store } = this.props; return <div>{store.data}</div>; } }
After:
javascriptimport { observer } from 'mobx-react-lite'; const Component = observer(() => { const store = useStore(); return <div>{store.data}</div>; });
3. Remove configure
Before:
javascriptimport { configure } from 'mobx'; configure({ enforceActions: 'always' });
After:
javascript// No longer need configure
4. Update extras
Before:
javascriptimport { extras } from 'mobx'; if (extras.isObservable(obj)) { // ... }
After:
javascriptimport { isObservable } from 'mobx'; if (isObservable(obj)) { // ... }
Best Practices
1. Use makeAutoObservable
javascriptclass Store { data = []; constructor() { makeAutoObservable(this); } }
2. Use mobx-react-lite
javascriptimport { observer } from 'mobx-react-lite'; const Component = observer(() => { return <div>{/* ... */}</div>; });
3. Use React Context
javascriptconst StoreContext = createContext(null); function StoreProvider({ children, store }) { return ( <StoreContext.Provider value={store}> {children} </StoreContext.Provider> ); } function useStore() { const store = useContext(StoreContext); if (!store) { throw new Error('useStore must be used within StoreProvider'); } return store; }
4. Use TypeScript
typescriptclass Store { data: Data[] = []; constructor() { makeAutoObservable<Store>(this); } }
Common Questions
1. How to continue using decorators?
If you need to continue using decorators, you can install the mobx-undecorate package.
javascriptimport { decorate, observable, action } from 'mobx'; class Store { data = []; addData(item) { this.data.push(item); } } decorate(Store, { data: observable, addData: action });
2. How to handle type inference?
When using makeAutoObservable, you can pass a generic parameter.
typescriptclass Store { data: Data[] = []; constructor() { makeAutoObservable<Store>(this); } }
3. How to handle action.bound?
When using action.bound, you need to specify it in makeObservable.
javascriptclass Store { data = []; constructor() { makeObservable(this, { data: observable, addData: action.bound }); } addData(item) { this.data.push(item); } }
Summary
- MobX 6 removed decorator support, recommending the use of
makeAutoObservable - Removed
configure,extras,intercept,observeand other APIs - Recommend using
mobx-react-liteand React Context - Better support for TypeScript
- Improvements in performance and error handling
- Migration is relatively simple, mainly replacing decorator syntax