MobX 6 with multiple stores using React hooks
Tags: mobx, react, webdev, tutorial
Published: 2021-02-20 17:33:58

When an app becomes bigger and more states to manage. We can add more stores making the store specific to their usage. In this post, I am going to share my approach to how to manage states with multiple stores to your react site. 

### Get started
Assumed you have a React app ready, we can install the necessary dependencies to work with MobX.

```sh
npm i mobx mobx-react
```

### Create stores
> We will create 2 stores, user and note store, user store will only have a user name as observable and a set function to modify the name. Note store will be keeping an array of notes. To display communication between stores, each note will have the user name who posts the note. Afterward, we can use the store to our app using hooks.

To begin, create a new folder called `stores` in the `src` directory
`/src/stores/`

#### Create user store
Add a new file inside stores folder `/src/stores/user.store.js`

```js
// src/stores/user.store.js

import { makeAutoObservable } from "mobx";

class userStore {
  name = "John doe";

  constructor() {
    makeAutoObservable(this);
  }

  setUserName = (name) => {
    this.name = name;
  };
}

export default userStore;
```
> the `makeAutoObservable` utility function introduced in MobX version 6.0 and above, you can use makeObservable/decorate if you use the older version of MobX.

#### Create note store
Add a new file inside stores folder `/src/stores/note.store.js`

```js
// src/stores/note.store.js

import { makeAutoObservable } from "mobx";

class noteStore {
    notes = [];

    constructor() {
      makeAutoObservable(this);
    }

    addNote(note) {
        let send_note = { note };

        this.notes.push(send_note);
    }
}

export default noteStore;
```

### Link the stores together
> We will create context hooks as the interface to connect to the stores using hooks and create a root/main store that will link all of our stores. Root store will also be used to communicate between stores

#### Create root store
Add an index file inside stores folder `/src/stores/index.js`

```js
// src/stores/index.js

import UserStore from "./user.store";
import NoteStore from "./note.store";

class RootStore {
  constructor() {
    this.userStore = new UserStore(this)
    this.noteStore = new NoteStore(this)
  }
}
```
> note that we are passing `this` to the stores so that every store will have access to other stores by accessing the context passed to their constructor

#### Communicate between stores
Modify note store `/src/stores/note.store.js` so that each note addition will have the user name who post it

```js
// src/stores/note.store.js

import { makeAutoObservable } from "mobx";

class noteStore {
    notes = [];
    
    // `this` from rootstore passed to the constructor and we can 
    // assign it to a variable accessible in this class called 
    // `rootStore`. Therefore, we can access other store like 
    // useStore for e.g (this.rootStore.userStore)
    constructor(rootStore) {
        this.rootStore = rootStore;
        makeAutoObservable(this);
    }

    addNote(note) {
        let send_note = { note };

        // check if name is available on userstore
        if (this.rootStore.userStore.name) {
          send_note.username = this.rootStore.userStore.name;
        }

        this.notes.push(send_note);
    }
}

export default noteStore;
```

#### Expose stores from context
Modify index `/src/stores/index.js` to use react context on the root store

```js
import React from "react";
import UserStore from "./user.store";
import NoteStore from "./note.store";

class RootStore {
  constructor() {
    this.userStore = new UserStore(this)
    this.noteStore = new NoteStore(this)
  }
}

const StoresContext = React.createContext(new RootStore());

// this will be the function available for the app to connect to the stores
export const useStores = () => React.useContext(StoresContext);
```

### Use the stores in the app
All store setup is now done, Great!. now it's time to use them in our app.

> We will create the component in `src/App.js` file to access the stores, the component will have two input fields, 
the first input field will be to change the name. the name changed will be reflected in a text above the input field.
the second input field will be to add a new note. note list can be found below the input field.

```js
// src/App.js

import { useState } from "react";
import { useObserver } from "mobx-react";
// this is the hook function we have made on `stores/index.js` to access all of our stores
import { useStores } from "./stores";

export default function App() {
  // here you can access all of the stores registered on the root store
  const { noteStore, userStore } = useStores();
  const [note, setNote] = useState("");

  // tracking the name change
  const handleNameChange = (e) => {
    e.preventDefault();
    const {
      target: { value }
    } = e;

    // access the user store set name action
    userStore.setUserName(value);
  };

  // tracking the note change
  const handleNoteChange = (e) => {
    e.preventDefault();
    const {
      target: { value }
    } = e;

    setNote(value);
  };

  const addNote = () => {
    // access the note store action adding note to the notes array
    noteStore.addNote(note);
  };

  // since we want to observe the change in name and list, useObserver is required, otherwise, we can straightaway return jsx
  return useObserver(() => (
    <div className="App">
      <h1>hello {userStore.name}</h1>

      <h2>Change your name here</h2>
      <input type="text" value={userStore.name} onChange={handleNameChange} />

      <h2>Insert note</h2>
      <input type="text" value={note} onChange={handleNoteChange} />
      <button type="button" onClick={addNote}>
        Add note
      </button>

      <h2>Note list</h2>
      {noteStore?.notes?.length ? (
        noteStore.notes.map((note, idx) => (
          <div key={idx}>
            <h3>from {note.username}</h3>
            <code>{note.note}</code>
          </div>
        ))
      ) : (
        <p>No note on the list</p>
      )}
    </div>
  ));
}
```

### :tada: End result

{% codesandbox epic-edison-p0ws6%}

### End notes
There are still many improvements that can be done from here, make stores that suit your needs, add more functions, handling APIs, persistent stores by saving them to local/session storage, and many more. Let's try those in the next post :wink: