Mastering Large-Scale React State Management with MobX
Getting Started
To begin, create a TypeScript React app using create-react-app. Install the necessary dependencies, including MobX, and delete unnecessary files. We’ll build a simple blog app with three entities: users, posts, and comments.
npx create-react-app my-app --template typescript
npm install mobx mobx-react
Defining Entity Types
Create types for each entity in a types
folder under src
. For example, user.ts
would contain the user type definition.
// src/types/user.ts
export interface User {
id: number;
name: string;
email: string;
}
Next, create types for posts and comments.
// src/types/post.ts
export interface Post {
id: number;
title: string;
content: string;
userId: number;
}
// src/types/comment.ts
export interface Comment {
id: number;
content: string;
postId: number;
}
The App Store
Create a folder called stores
under src
and define the app store in app.ts
. This file will contain all the stores of the app. For now, make it an empty class.
// src/stores/app.ts
export class AppStore {}
Creating Models
Create a folder called models
under src
and implement the models for each entity. Each model will implement the corresponding type and define relationships with other entities.
// src/models/user.ts
import { User } from '../types/user';
import { AppStore } from '../stores/app';
export class UserModel {
private appStore: AppStore;
private user: User;
constructor(appStore: AppStore, user: User) {
this.appStore = appStore;
this.user = user;
}
// Define relationships and methods here
}
Creating Stores in MobX
Create stores for each entity, starting with the user store.
// src/stores/userStore.ts
import { observable, action } from 'obx';
import { UserModel } from '../models/user';
export class UserStore {
@observable users: UserModel[] = [];
@action
load(): void {
// Load data into the store
}
get allUsers(): UserModel[] {
return this.users;
}
}
Defining Relationships
Define relationships between models. For example, the user model will have many posts, which can be coded as a computed property derived from the post store.
// src/models/user.ts
import { PostModel } from './post';
export class UserModel {
//...
get posts(): PostModel[] {
return this.appStore.postStore.allPosts.filter((post) => post.userId === this.user.id);
}
}
Coding the Network Layer
Create an AppApi
class to handle network calls and load data into stores. Separate the network layer from the stores, so the store doesn’t know where the data is loaded from.
// src/api/appApi.ts
import axios from 'axios';
export class AppApi {
private apiUrl: string;
constructor(apiUrl: string) {
this.apiUrl = apiUrl;
}
loadUsers(): Promise<user[]> {
return axios.get(`${this.apiUrl}/users`);
}
// Create API clients for each resource
}</user[]>
App Context
Use React Context to provide the store and API to components.
// src/context/appContext.ts
import { createContext, useState } from 'eact';
import { AppStore } from '../stores/app';
import { AppApi } from '../api/appApi';
interface AppContext {
store: AppStore;
api: AppApi;
}
const AppContext = createContext({ store: new AppStore(), api: new AppApi('https://api.example.com') });
export default AppContext;
Using Components with MobX
Create components for each page, such as the home page, post page, and user page. Use the useAppContext
hook to get the store and API, and wrap components with observer
from mobx-react
to observe changes to observables from the store.
// src/components/HomePage.tsx
import React from 'eact';
import { observer } from 'obx-react';
import { useAppContext } from '../context/appContext';
const HomePage: React.FC = () => {
const { store, api } = useAppContext();
// Use the store and API here
return (
);
};
export default observer(HomePage);
Building Pages
Create pages for the app, such as the home page, post page, and user page. Use the useAppContext
hook to get the store and API, and load data into the store using the API clients.
// src/pages/HomePage.tsx
import React, { useEffect } from 'eact';
import { useAppContext } from '../context/appContext';
const HomePage: React.FC = () => {
const { store, api } = useAppContext();
useEffect(() => {
api.loadUsers().then((users) => store.userStore.load(users));
}, [store, api]);
// Use the store and API here
return (
);
};
The Root App Component
Create the root app component, App.tsx
, to instantiate the store and API, and provide it to the app through AppContext.Provider
. Use BrowserRouter
from react-router-dom
to render pages.
// src/App.tsx
import React from 'eact';
import { BrowserRouter } from 'eact-router-dom';
import AppContextProvider from './context/appContext';
import HomePage from './pages/HomePage';
const App: React.FC = () => {
return (
);
};
export default App;