
State management is the core of any React application and as React is just a UI library, we need something to take care of the state of our app. State management can become troublesome and it is easy to create unmanageable React applications because of inconsistent state.
In this article, we will learn about how to use MobX as our state management solution in a React Native application.
A State is just the data that your app is dealing with. State saves the data that a component requires and it influences how the component gets rendered. State management is the process of managing that data. Monitoring and retrieving data in a particular app can be difficult and that’s where state management libraries come to the rescue. There are multiple ways to manage states like using Redux or the React Context API, but here we’ll cover MobX.
MobX is a state management library that can be used with any JavaScript framework. React and MobX are powerful together and work as a complete framework. MobX provides the mechanism to store and update the application state that React Native then uses to render the components. The Philosophy behind MobX is: *“Anything that can be derived from the application state, should be derived. Automatically.”*
Derivations form the backbone of MobX which enables us to discard the repeated state. The idea is to:
Find minimal state (observable state), derive everything (derived state) and never turn the state into more state.
MobX at its core has three important concepts: Observables, Actions, and Reactions. A Store contains these three which then is used by the React Native application.
Observables with MobX are just containers that hold the core state of our application. The idea is to make an object able to emit new changes to which the observer can react. You can achieve this with the @observable decorator.
Let’s imagine we have a variable named count that changes over time. We can make it observable simply by:
// import observable from mobx
import { observable } from "mobx";
//create a store with count observable
class Store {
@observable
count = 0;
}
//export Store
export default new Store();
Remember the principle of MobX, *“Find minimal state (observable state), derive everything (derived state)”*.
The values that can be derived from already defined observables are computed values. MobX avoids inconsistency of state by discouraging the creation of more states. Imagine our count variable holds the number of minutes by which something is delayed. We can add a computed delay message that gets derived from the observable count.
import { observable, computed } from "mobx";
class Store {
@observable
count = 0;
@computed
get delayMessage = () => {
return 'The train is delayed by' + this.count;
};
}
export default new Store();
Here, @computed is working as a getter function deriving its value from count. delayMessage will automatically emit changes as the value of count changes.
Actions are simply functions that modify the state. MobX supports uni-directional data flow, it means that once the action changes state, it automatically updates all the views which are consuming that state. Let’s add an action that updates over the count variable as the delay increases.
Store {
import { observable, computed, action } from "mobx";
class Store {
@observable
count = 0;
@computed
get delayMessage = () => {
return 'The train is delayed by' + this.count;
};
@action
updateDelay = delay => {
this.count = delay;
};
}
export default new Store();
Note that all state modifications must be done by actions only.
An observer subscribes to any change in observables and re-renders the components which use them. Reactions are just side effects of these state changes. It’s very similar to computed values but the difference is instead of computing and returning a value, a reaction simply performs a side operation. In simple words, Reactions are:
Side effects that should occur in reaction to state changes (component re-render)
MobX provides three main types of reaction functions: autorun, when and reaction.
autorun is simply a function that runs every time the state changes.
autorun(() => {
console.log('delay is', this.count);
} );
The function runs each time the value of count changes. The key is that we are not explicitly stating that it has to watch for changes in the count variable. The fact that we have used count inside autorun makes it as one of its dependency and that’s enough to trigger the function whenever the dependency changes.
when triggers a side-effect whenever a certain condition is met. It takes two parameters. The first parameter is a function that gets reevaluated until it returns true and the second parameter is another function that runs once the first function returns true. A simple example could be:
class MyResource {
constructor() {
when(
// once...
() => this.count > 60,
// ... then
() => console.log("Guest is too late, maybe he's not coming");
);
}
}
Here, when simply checks if the delay is more than an hour (60 minutes) then prints that he might not be coming.
reaction is a variation of autorun which gives more control over the data (dependency) used in the function. It accepts two function arguments and a third argument of options:
The following example shows a reaction that is invoked only once.
const reactionDemo = reaction(
() => this.count,
(count, reaction) => {
console.log("reaction demo: invoked. delay is " + count);
reaction.dispose();
}
);
this.count = 1;
// prints:
// reaction demo: invoked. delay is = 1
this.count = 2;
// prints:
// (There are no logging, because of reaction disposed. But, count continue reaction)
console.log(this.count);
// prints:
// 2
We’ll understand the working of MobX by creating a React Native app in three simple steps:
Here we’re building a simple app that gets images from Unsplash and shows them to the user. Users can also click on an image and add it to their favorites.
The application uses the Unsplash API to fetch random images. You can generate an API key here.
If you have not created a project yet then follow the steps below:
$ react-native init UnsplashDemo
or, using Expo:
$ expo init UnsplashDemo
$ npm install mobx mobx-react
$ react-native run-<your-os>
Or, using Expo:
$ expo start
We’re going to search for some images and save the result. Then, we’ll allow clicking on any image to add it to our favorites. Comments are self-explanatory:
// importing observables and decorate
import { decorate, observable, action } from "mobx";
class Store {
// observable to save search query
text = '';
// action to update text
updateText = (text) => {
this.text = text;
}
// observable to save image response from api
data = null;
// action to call API and search images
searchImages = () => {
fetch(`https://api.unsplash.com/search/photos?client_id=${API_KEY}&page=1&query=${this.text}&orientation=landscape`)
.then(response => response.json())
.then(data => this.setData(data));
};
// observables can be modifies by an action only
setData = (data) => {
this.data = data;
};
}
// another way to decorate variables with observable
decorate(Store, {
text: observable,
updateText: action,
data: observable,
searchImage: action,
setData: action,
});
// export class
export default new Store();
Create a component ImageList.js that will render the list of images. It will also show the images added to our favorites with a simple switch toggle.
import React from "react";
import { View, TextInput, Button, FlatList } from 'react-native';
// imports inject and observer from 'mobx-react':
import { inject, observer } from "mobx-react";
// components receive Store values as props which we will inject while exporting
function ImageList(props) {
// destructure variables from store to use
const { text, updateText, data, searchImages } = props.store;
return (
<>
<TextInput // TextInput to get search query from user
style={styles.input}
value={text}
onChangeText={updateText}
/>
<Button // Button to call API
title="Search"
style={styles.button}
onPress={searchImages}
/>
/>
<FlatList
data={data.results} // response from API
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<ImageView // reusable component to render image
source={{ uri: item.urls.small }} // passing the url
onPress={() => {}} // action to add item to favorite
/>
)}
/>
</>
);
}
// inject Store as props to ImageList and make it observe changes in Store
export default inject("store")(observer(ImageList));
We are just taking input from TextInput and calling the Unsplash search API by pressing the Button. The response is getting saved in the data observable and we are using that in the FlatList component to render a list of images. Simple, right? Now let’s move on to adding images to our favorites.
Go to unsplash.com/developers to learn more about the Unsplash API response.
As we know, actions are responsible for modifying state. So far, the updateText mutated the text observable and setData mutated the data observable. Now we want to add images to our favorites, it means that we need one observable to store the state and one action to mutate this state. Let’s add them.
import { decorate, observable, action } from "mobx";
class Store {
text = '';
updateText = (text) => {...};
data = null;
searchImages = () => {...};
setData = (data) => {...};
// array to save favourite images
favorites = [];
// action to add images to favorites
addToFavorite = (image) => {
this.favorites.push(image);
this.data = null;
this.text = '';
};
}
decorate(Store, {
text: observable,
updateText: action,
data: observable,
searchImage: action,
setData: action,
//adding decorators
favorites: observable,
addToFavorite: action,
});
export default new Store();
Now we’ll update our View for these added observables and actions. We want to clear previously searched images and show the added favorite image, this can be done simply by:
// previous destructuring
const { favorite, addToFavorite} = this.props.store
return (
<>
{/* TextInput and Button added earlier */}
{/* If data is available then show search results otherwise show the favorite images */}
{data ?
<FlatList // To render list of images
style={styles.container}
data={data.results}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<ImageView
source={{ uri: item.urls.small }}
onPress={() => addToFavorite(item.urls.small)} // action to add url to favorite
/>
)}
/> :
<FlatList
style={styles.container}
data={favorites}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => (
<ImageView
source={{ uri: item }} // render favorite images
/>
)}
/>
}
</>
);
We’ve used observers, observables and actions so far. Let’s add a computed to show the number of images added to favorites. computed works like a getter function to get derived state from the observable. It can be added as:
import { decorate, observable, action, computed } from "mobx";
class Store {
// previously added value
get getFavoriteCount() {
return this.favorites.length;
}
}
decorate(Store, {
// previously added values
getFavoriteCount: computed,
});
export default new Store();
Let’s quickly add it to our View also:
const { getFavoriteCount } = this.props.store;
return (
// TextInput, Button
<Text style={styles.count}>
Images added: {getFavoriteCount}
</Text>
// FlatList
);
Now, the last thing we have to do is provide store in Provider to the root component. Our root file will look like this:
import React from 'react';
import ImageList from './src/container/ImageList';
// imports Provider and store
import { Provider } from 'mobx-react';
import store from './src/store';
const App = () => {
return (
<Provider store={store}>
<ImageList />
</Provider>
);
};
export default App;
That’s it. Let’s see a GIF of our app in action:

We’ve learned about observables, actions, observers, and computed properties in MobX and successfully used them by building a simple React Native app. I hope you had fun learning MobX and this tutorial was helpful in getting you started with React Native + MobX. Happy coding! 👨💻
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Thanks for the this article. Can you share github repo for this? There are few missing peaces and typos that are causing errors while implementing this.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
Full documentation for every DigitalOcean product.
The Wave has everything you need to know about building a business, from raising funding to marketing your product.
Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.
New accounts only. By submitting your email you agree to our Privacy Policy
Scale up as you grow — whether you're running one virtual machine or ten thousand.
Sign up and get $200 in credit for your first 60 days with DigitalOcean.*
*This promotional offer applies to new accounts only.