MobX with React and Typescript in 2023. How to implement it in a good, clear and effective way.
I’ve got a recruitment task about using MobX, I’ve never done it before. So I started looking for information on the internet and mostly found some old resources with old react. What I haven’t seen is how to use it in a nice modern way in the current version of React with hooks. It took me a few additional hours of research to find it.
- Create Store / Stores
The store is a central part of your state, where all your data is stored and “muted” etc. Not like Redux, in MobX you can create many stores. But later could be a problem to access from one store to another etc. The best-looking solution I’ve founded is to use one RootStore. Below I will present a simple example with Todos:
// /store/store.ts
// Create RootStore, that will contain references to other stores
export class RootStore {
userStore: UserStore;
todosStore: TodoStore;
constructor() {
// Bcause of passing this to sub stores, all stores can access RootStore and other stores
this.userStore = new UserStore(this);
this.todosStore = new TodoStore(this);
}
}
class UserStore {
rootStore: RootStore;
@observable userId: string = '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d';
// RootStore is passed in constructor
constructor(rootStore: RootStore) {
makeAutoObservable(this, { rootStore: false });
// Assign it to local variable so can access to it
this.rootStore = rootStore;
}
@computed get todos(): TodoStore[] {
// Access todoStore through the root store.
// In code below there is no author value in Todos, this was just created for show purpose
return this.rootStore.todoStore.todos.filter(todo => todo.author === this.userId)
}
}
class TodoStore {
rootStore: RootStore;
// To define shape and mutations in you can create additional TodoStore
@observable.shallow todos: TodoStore[] = [];
constructor(rootStore: RootStore) {
makeAutoObservable(this, { rootStore: false });
this.rootStore = rootStore;
}
...here should be more todos properties and actions...
}
// inititate and export store so you can use it in the app
export const store = new RootStore();
Above you can see an example of the good shape of the store in Typescript, and also one store has access to another, so ex. can show todos only for one user.
2. Pass store to React
Actually, using the code above you can just import store variable and use it in react component. But I believe a better way is to inject it into context, so you can for example mock it in tests.
// Create store context
// /context/store.ts
import { createContext } from 'react';
import { RootStore } from '../store/store';
export const StoreContext = createContext<RootStore>({} as RootStore);
export const StoreProvider = StoreContext.Provider;
// Create hook to consume context in easy way
// /hooks/useStore.ts
import { useContext } from 'react';
import { StoreContext } from '../context/store';
import { RootStore } from '../store/store';
export const useStore = (): RootStore => useContext(StoreContext);
// Add context to the app
// /index.tsx
...
import { store } from './store/store';
...
<StoreContext.Provider value={store}>
<App />
</StoreContext.Provider>
...
3. Consume store
After passing store to context it is really easy to get all data from there. What is important, in MobX every time observer (HOC) needs to be used. If forget about HOC, when change data, this data will be not refreshed in a component.
// /components/SomeComponent.tsx
import { observer } from 'mobx-react-lite';
import { useStore } from '../hooks/useStore';
// It is really important to add observer HOC, so data is always updated!
export const SomeComponent = observer(() => {
const store = useStore();
const { todos } = store.userStore;
return (
<div>
{JSON.stringify(todos)}
</div>
);
});
And that was the last part about getting data from MobX. In this article, you’ve read about how to create a store, how to implement Typescript with MobX, how to connect a few stores to one and how to consume data in React with hooks. Hope that this information will be useful for you. If you have some questions or comments please feel free to comment this article.