DEV Community

loading...
Cover image for MobX 6 with multiple stores using React hooks

MobX 6 with multiple stores using React hooks

cakasuma profile image Mustofa Ghaleb Amami ・4 min read

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.

npm i mobx mobx-react
Enter fullscreen mode Exit fullscreen mode

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

// 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;
Enter fullscreen mode Exit fullscreen mode

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

// 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;
Enter fullscreen mode Exit fullscreen mode

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

// 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)
  }
}
Enter fullscreen mode Exit fullscreen mode

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

// 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;
Enter fullscreen mode Exit fullscreen mode

Expose stores from context

Modify index /src/stores/index.js to use react context on the root store

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);
Enter fullscreen mode Exit fullscreen mode

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.

// 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>
  ));
}
Enter fullscreen mode Exit fullscreen mode

🎉 End result

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 😉

Discussion (0)

pic
Editor guide