Categories
Mobx

State Management with MobX 6 — Do Things When an Observable Meets a Given Condition

MobX is a simple state management solution for JavaScript apps.

In this article, we’ll look at how to use MobX 6 to add state management into our JavaScript apps.

Do Things When an Observable Meets a Given Condition

We can use the when function provided by MobX 6 to do something when the store’s state is in a given condition.

For instance, we can write:

import { makeObservable, observable, computed, action, when } from "mobx";

class Count {
  count = 0;
  get doubleCount() {
    return this.count * 2;
  }
  constructor(count) {
    makeObservable(this, {
      count: observable,
      doubleCount: computed,
      increment: action
    });
    this.count = count;
  }

  increment() {
    this.count++;
  }
}

const store = new Count(1);

when(
  () => store.count === 3,
  () => console.log(`It's 3`)
);

store.increment();
store.increment();

We create a store with the count observable, doubleCount computed state, and the increment action.

Then we instantiate it and assign the returned object to the store .

Next, we call when with a function that returns the condition that we want to look for in the store.

The 2nd callback runs when the condition we return in the function we passed in as the first argument is met.

Since we called increment twice and the store’s count state is initialized to 1, we should see the console log run.

If we don’t pass in a 2nd argument into when , it returns a promise.

So we can use it to wait for a given condition and then run code after that.

For instance, we can write:

import { makeObservable, observable, computed, action, when } from "mobx";

class Count {
  count = 0;
  get doubleCount() {
    return this.count * 2;
  }
  constructor(count) {
    makeObservable(this, {
      count: observable,
      doubleCount: computed,
      increment: action
    });
    this.count = count;
  }

  increment() {
    this.count++;
  }
}

const store = new Count(1);

const when3 = async () => {
  await when(() => store.count === 3);
  console.log(`It's 3`);
};

when3();
store.increment();
store.increment();

We have the same store as in the previous example.

But below that, we have the when3 function that calls when with the same function as before as the first argument.

The 2nd function is omitted.

Then we call the console.log after that.

Then we run the when3 function.

Next, we call increment twice as before.

So we should see the console log run after count reaches 3 as we specified in the when3 function.

Conclusion

We can use the when function provided by MobX 6 to do something after a store’s state meets the condition we’re looking for.

Categories
Mobx

State Management with MobX 6 — Disposing Autorun and Watching Observables

MobX is a simple state management solution for JavaScript apps.

In this article, we’ll look at how to use MobX 6 to add state management into our JavaScript apps.

Disposing Autorun

When we don’t need to watch a store for changes anymore, we should dispose of any watchers we created.

To do this, we can call the function returned by autorun , which returns a function that lets us remove it from memory when we’re done using it.

For instance, we can write:

import { makeObservable, observable, computed, action, autorun } from "mobx";

class Count {
  count = 0;
  get doubleCount() {
    return this.count * 2;
  }
  constructor(count) {
    makeObservable(this, {
      count: observable,
      doubleCount: computed,
      increment: action
    });
    this.count = count;
  }

  increment() {
    this.count++;
  }
}

const store = new Count(1);

const dispose = autorun(() => {
  console.log(store.count);
});

store.increment();
dispose();

We create a store with the count observable, doubleCount computed state, and the increment action.

Then we instantiate it and assign the returned object to the store .

Next, we call autorun to watch the values we want.

We assign the returned function to the dispose variable.

Then we call the store.increment action to increment the count state value which will trigger the autorun callback to run.

Then we call dispose to remove the watcher created by autorun .

Watching Observables

In addition to using the autorun function to watch MobX store states, we can use the reaction function.

It takes a function that returns the value we want to watch as the first argument.

The first argument is another function that has the current and previous value being observed as the parameters.

And then we can do something with them in the callback.

For instance, we can write:

import {
  makeObservable,
  observable,
  computed,
  action,
  autorun,
  reaction
} from "mobx";

class Count {
  count = 0;
  get doubleCount() {
    return this.count * 2;
  }
  constructor(count) {
    makeObservable(this, {
      count: observable,
      doubleCount: computed,
      increment: action
    });
    this.count = count;
  }

  increment() {
    this.count++;
  }
}

const store = new Count(1);

reaction(
  () => store.count,
  (currCount, prevCount) => {
    console.log(currCount, prevCount);
  }
);

store.increment();

We have the same store as before.

But we call the reaction function with the 2 callbacks.

The 1srt callback returns the value of the store.count state.

The 2nd callback has the current and previous values of store.count respectively.

The 2nd callback will run when we call store.increment .

This lets us have more fine-grained control of what we want to watch in the store.

Conclusion

We can watch values with more fine-grained control with the reaction function provided by MobX.

Also, we should dispose of watchers created by the autorun function when we no longer need them.

Categories
Mobx

State Management with MobX 6 — Creating Observable States

MobX is a simple state management solution for JavaScript apps.

In this article, we’ll look at how to use MobX 6 to add state management into our JavaScript apps.

Creating Observable State

We can create one or more observable states by creating a store.

To do this, we write:

import { makeObservable, observable, computed, action, autorun } from "mobx";

class Count {
  count = 0;
  get doubleCount() {
    return this.count * 2;
  }
  constructor(count) {
    makeObservable(this, {
      count: observable,
      doubleCount: computed,
      increment: action
    });
    this.count = count;
  }

  increment() {
    this.count++;
  }
}

const store = new Count(1);

autorun(() => {
  console.log(store.count);
});

store.increment();

We have the Count class that has the count and doubleCount states.

count is an observable state that we can manipulate.

We add this by setting the count property to observable in the makeObservable call.

The doubleCount getter is used to create a computed state, which is a state derived from other observable states.

The increment method is an action as indicated by setting increment to action .

We manipulate observable state values in actions.

In the constructor, we set this.count to count to initialize it.

Next, we create the Count instance, which is the store.

Then we call autorun with a callback to get the store.count value.

The callback will run whenever a state updates.

So when store.increment is called, the callback will run to log the latest value of store.count .

This also works with computed states, so we can write:

import { makeObservable, observable, computed, action, autorun } from "mobx";

class Count {
  count = 0;
  get doubleCount() {
    return this.count * 2;
  }
  constructor(count) {
    makeObservable(this, {
      count: observable,
      doubleCount: computed,
      increment: action
    });
    this.count = count;
  }

  increment() {
    this.count++;
  }
}

const store = new Count(1);

autorun(() => {
  console.log(store.count);
  console.log(store.doubleCount);
});

store.increment();

to watch the value of both states in the autorun callback.

Conclusion

We can create a data store for any client-side JavaScript app with MobX.

Categories
Mobx

Getting Started with React State Management with MobX 6

MobX is a simple state management solution for React apps.

In this article, we’ll look at how to use MobX 6 to add state management into our React apps.

Installation

To get started, we can install the MobX package by running:

yarn add mobx

with Yarn or:

npm install --save mobx

with NPM.

To use it with React, we also need the mobx-react-lite package.

To install it, we run:

npm i mobx-react-lite

We can add a script tag to load the MobX library from CDN.

The script’s URL is:

https://cdnjs.com/libraries/mobx

or:

https://unpkg.com/mobx/dist/mobx.umd.production.min.js

Simple Usage

After we installed the packages, we can use them to store global states of our React app.

For instance, we can write:

import React from "react";
import { makeObservable, observable, computed, action } from "mobx";
import { observer } from "mobx-react-lite";

class Count {
  count = 0;
  get doubleCount() {
    return this.count * 2;
  }
  constructor(count) {
    makeObservable(this, {
      count: observable,
      doubleCount: computed,
      increment: action
    });
    this.count = count;
  }

increment() {
    this.count++;
  }
}

const store = new Count(1);

const Counter = observer(({ store: { count } }) => {
  return <div>{count}</div>;
});

const DoubleCounter = observer(({ store: { doubleCount } }) => (
  <div>{doubleCount}</div>
));

const Incrementer = observer(({ store }) => {
  return (
    <div>
      <button onClick={() => store.increment()}>increment</button>
    </div>
  );
});

export default function App() {
  return (
    <div>
      <Incrementer store={store} />
      <Counter store={store} />
      <DoubleCounter store={store} />
    </div>
  );
}

to create the Count store and use it in our React app.

We have the count state in the Count class.

doubleCount is a state that’s computed from the value of the count state.

In the constructor, we have makeObservable function to make the count and doubleCount properties reactive states.

observable makes it an observable state.

And computed makes it a computed state, which means it’s a reactive state derived from one or more observable states.

Then we initialize the this.count class property to the value of the count parameter.

Next, we create the increment method to increment the count value.

Then we create a store from the Count class and initialize the count state to 1.

Next, we create the Counter component which gets the store.count value and render it.

We also have the DoubleCounter component to render the store.doubleCount value.

Next, we have the Incrementer component that gets the store from the prop and call its increment to increment the count value when we click the button.

We wrap all 3 components with the MobX observer function to make all 3 components watch the store state values.

Finally, in App , we add all 3 components and pass in the store so that when we click increment, we see both count and doubleCount update.

Conclusion

We can add state management to our React app with MobX easily.

Categories
Mobx

Reacting to Observables with MobX-React

We can use MobX with MobX-React to manage the state by watching MobX observables and changing the observable data.

In this article, we’ll look at how to create an Observable and use it in React components directly.

Creating Observables and Using Them in React Components

We have to install the mobx and mobx-react packages to create Observables and use them within React components.

To do this, we can write the following:

npm i mobx mobx-react

Then we can create our Observable object and a React component that uses it as follows:

import React from "react";
import ReactDOM from "react-dom";
import { observer } from "mobx-react";
import { observable } from "mobx";

const countData = observable({
  count: 0
});

const App = observer(({ countData }) => (
  <>
    <button onClick={() => countData.count++}>Increment</button>
    <p>{countData.count}</p>
  </>
));
const rootElement = document.getElementById("root");
ReactDOM.render(<App countData={countData} />, rootElement);

In the code above, we created the countData Observable object by writing:

const countData = observable({
  count: 0
});

We’ll be able to get the latest state within our React component and then pass it in as a prop for our React component.

To make our React component watch for the latest value from our Observable object and let us change its value from within the component, we write:

const App = observer(({ countData }) => (
  <>
    <button onClick={() => countData.count++}>Increment</button>
    <p>{countData.count}</p>
  </>
));
const rootElement = document.getElementById("root");
ReactDOM.render(<App countData={countData} />, rootElement);

The code above gets the countData prop from the countData Observable. Then in the onClick handler, we just pass in the function to change its value.

Then when we reference the App component in the last line, we just pass in the countData Observable object as the value of the countData prop.

countData.count++ will change the state of the Observable, which then the latest value will be obtained from the prop.

We can use the countData Observable object with class components as follows:

import React from "react";
import ReactDOM from "react-dom";
import { observer } from "mobx-react";
import { observable } from "mobx";

const countData = observable({
  count: 0
});

@observer
class App extends React.Component {
  render() {
    return (
      <>
        <button onClick={() => countData.count++}>Increment</button>
        <p>{countData.count}</p>
      </>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App countData={countData} />, rootElement);

The only difference is that it’s a class component with a render method and that we use the observer decorator instead of the observer function.

Using Context to Pass Observables Around

We can use the React Context API to pass the values of an Observable to another component.

For instance, we can write the following code:

import React, { useContext } from "react";
import ReactDOM from "react-dom";
import { observer } from "mobx-react";
import { observable } from "mobx";

const countData = observable({
  count: 0
});

const CountContext = React.createContext();

const Counter = observer(() => {
  const countData = useContext(CountContext);
  return (
    <>
      <button onClick={() => countData.count++}>Increment</button>
      <p>{countData.count}</p>
    </>
  );
});

const App = ({ countData }) => (
  <CountContext.Provider value={countData}>
    <Counter />
  </CountContext.Provider>
);

const rootElement = document.getElementById("root");
ReactDOM.render(<App countData={countData} />, rootElement);

In the code above, we have the CountContext . We create it by writing:

const CountContext = React.createContext();

Then in App , we included the CountContext component, so that we can get the countData Observable value by passing in the countData prop with the value set to the countData Observable.

In the Counter component, we call the observer function with a function component inside.

Inside the component, we use the useContext hook to get the value from CountContext .

Then in the handler function, we passed into the onClick prop of the button, we changed the count ‘s value, which will automatically change the value in the countData Observable object and set as the value of countData.count .

Therefore, when we click the Increment button, the number below it will go up.

Storing Observables in Local Component State

We can also use Observables as a local state in a React component.

To do this, we can use the useState hook by passing in a function that returns an Observable object as follows:

import React, { useState } from "react";
import ReactDOM from "react-dom";
import { observer } from "mobx-react";
import { observable } from "mobx";

const App = observer(() => {
  const [countData] = useState(() =>
    observable({
      count: 0
    })
  );

  return (
    <>
      <button onClick={() => countData.count++}>Increment</button>
      <p>{countData.count}</p>
    </>
  );
});
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

In the code above, we used the useState hook by passing in a function that returns an Observable object with the count property. We then assigned that to the countData variable.

Once we did that, countData.count will be updated by the onClick handler and when we click the Increment button, we’ll see the countData.count value update automatically.

If we click the Increment button, then the value will go up.

We don’t really need to use MobX Observable objects to store local state unless complex computations are involved, since MobX will optimize for those.

Likewise, we can use MobX to store local state with class components as follows:

import React, { useState } from "react";
import ReactDOM from "react-dom";
import { observer } from "mobx-react";
import { observable } from "mobx";

@observer
class App extends React.Component {
  @observable count = 0;

  render() {
    return (
      <>
        <button onClick={() => this.count++}>Increment</button>
        <p>{this.count}</p>
      </>
    );
  }
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

In the code above, we have this.count , which is an Observable field. In the onClick listener, we just modify this.count directly, and then it’ll be reflected in the this.count in between the p tag.

@observer implements memo orshouldComponentUpdate automatically so that there won’t be any unnecessary re-renders.

Conclusion

We can use MobX Observable to store a React app’s state or a React component’s state.

To use it for storing React app’s state, we can either pass an Observable to a component as a prop or we can use the Context API to send the data to different components.

We can also use it to store local state by either using the useState hook in function components and using the observable decorator within a class component.