First, after much thought and consideration, Mobx was chosen as the status management tool for the new project. The main reasons were that Mobx implemented responsive programming, the code was more lightweight and simple, and the natural support for independent coexistence of multiple stores.

This is the first day of my participation in the August Challenge. For details, see:August is more challenging

In fact, it takes a lot of struggle and a lot of thinking to pick the one you like best. This is probably the best practice for data flow in large, complex projects. With the theoretical guidance of the big guy, MY confidence doubled immediately.

Start by designing the domain model

Let’s take a simple e-commerce system as an example. For example, we need to pay attention to several areas:

  • User (saves information about the current user)
  • Orders (save current order information)
  • Notifications (need to display the latest notification information, either from the server side or from the user page for some actions following the order)

We want to implement these interactions:

  • Add a record to the notification Store after clicking the order successfully
  • Notice or order can be displayed only after the user has logged in (authenticated).

We can design the store hierarchy according to the requirements:

  • Global-store (provides authentication status, token information, and notification)
    • User (including user details and login and logout methods)
    • The order

Note: We have a convention that child stores can only interact with parent stores, no sibling or cross-reference, and if there is a need for cross-invocation, some of the data or state should be promoted to a common parent.

At this point, we’re just talking about the design, but let’s demonstrate the implementation details in code

The project of actual combat

First configure the public store (share-store)

// share-store
import { makeObservable, observable, action } from "mobx"

type LoginStatus = undefined|true|false;
export class ShareStore {
    @observable authStatus:boolean = false;
    @observable token:string = "";
    @observable notifyList:Array<string> = [];

    constructor() {
        makeObservable(this);
    }

    //update loginStatus
    @action
    updateAuthStatus(state:boolean) {
        this.authStatus = state;
    }
    @action 
    updateToken(token:string){
        this.token = token;
    }
    @action addNotify(notify:string){
        this.notifyList.push(notify)
    }
}
export const shareStore = new ShareStore();
Copy the code

Then the order – store:

//order-store
import { makeObservable, observable, action } from "mobx"
import {shareStore,ShareStore} from '.. /shared-store'
type orderStatusType = "none"|"progressing"|"done"
export class OrderStore {
    @observable orderStatus:orderStatusType = "none"
    @observable orderName:string = "";
    @observable orderType:"Book"|"Computer"|"" = "";
    @observable rootStore:ShareStore;
    
    constructor() {
        makeObservable(this);
        // Inject the public store
        this.rootStore = shareStore;
    }
    @action
    updateOrderStatus(state:orderStatusType) {
        this.orderStatus = state;
    }
    @action
    updateOrderInfo(state:any) {
        this.orderType = state.type;
        this.orderName = state.name;
    }
    @action
    doOrder(payload:any){
        //api request
        //....
        const msgName = "A new order occurs";
        this.rootStore.addNotify(msgName); }}export const orderStore = new OrderStore();
Copy the code

And then user-store:

//user-store
import { makeObservable, observable, action } from "mobx"
import {shareStore,ShareStore} from '.. /shared-store'
type UserInfoType = {
    name:string,
    gender:string,
    level:number
}
export class UserStore {
    @observable userInfo:UserInfoType = {
        name:"".gender:"".level:0
    }
    @observable rootStore:ShareStore;
    
    constructor() {
        makeObservable(this);
        this.rootStore = shareStore;
    }
    @action
    updateUserInfo(state:UserInfoType) {
        this.userInfo = state;
    }
    @action 
    doLogin(userName:string,password:string) {
        //Api request send
        //....
        const result = {
            name:"Andrew".gender:"male".level:100
        }
        this.updateUserInfo(result)
    }
    @action
    doLogout(){
        //get token 
        const token = this.rootStore.token;
        //Api request send
        //....
    
        this.rootStore.updateAuthStatus(false)}}export const userStore = new UserStore();
Copy the code

It is better to configure the store entry file with the contextAPI to provide the provider:

//index.ts
import {shareStore,ShareStore} from './shared-store'
import {orderStore,OrderStore} from './order/order'
import {userStore,UserStore} from './user/user'
import {createContext, useContext} from 'react'

interface StoreType {
    shareStore:ShareStore,
    orderStore:OrderStore,
    userStore:UserStore
}

const store:StoreType = {
    shareStore,
    orderStore,
    userStore
}
const Context = createContext<Partial<StoreType>>({});


export const StoreProvider:React.FunctionComponent<{}> = ({ children }) = > {
    return (<Context.Provider value={store}>{children}</Context.Provider>)};export const useStore = () = > useContext(Context);
Copy the code

Component consumption:

import {StoreProvider} from './store/index'

<StoreProvider>
  <App />
</StoreProvider>
Copy the code

The last

Thank you for reading. If you have any questions, please leave a comment. Thank you!