Demo: Using MobX to render different views based on stores in your React App

Short and simple example that illustrates the benefits of MobX!

Preview of finished product, feel free & play around with it here: https://jsfiddle.net/xsd7upv8/14/

Let’s say we have an app keeps track of a list of all the dogs we’ve ever pet. This basic application will have 2 different views:

  1. The view of our list of dogs we already pet
  2. The view for adding a new doggo to our list

Our app should change & render differently based on which view our application currently has. So how can we do this with MobX?

1. Making a store to keep track of your data

For any dynamic application that takes in user input, we need a way to keep track of these actions & events.

In our case, if we meet a new dog and want to add him to the list, we need some way to keep track of all the dogs’ names. In addition, we must also keep track of which view to show in our application based on certain user actions.

In MobX, an observable store is a way for you to make data ‘observable’ by automatically triggering your listening components to update whenever there is a change in state.

For our dog list application, we can declare a class and make it a store for our list of doggos. The plain class (without any MobX) might looks something like this:

class DogStore{
  dogList = ['Pluto', 'Oliver', 'Humphrey']; 
view = 'listView';
  addDog(dog){
this.dogList.push(dog);
}
  setView(view){
this.view = view;
}
}

We’ve also included ‘addDog’ and ‘setView’ which will be handy methods later on for when we want to update our data.

To include MobX here, all we need to do is:

  1. add the ‘@observable’ decorator in front of anything we want to be able to track
  2. add the ‘@action’ decorator in front of any class method that will change the state of our stores and require our components to update
class DogStore{
  @observable dogList = ['Pluto', 'Oliver', 'Humphrey'];
@observable view = 'listView';
  @action addDog(dog){
this.dogList.push(dog);
}
  @action setView(view){
this.view = view;
}
}

2. Connecting the store with components using MobX Provider, inject, & observer

In this app, one of our main component will be the list of names that renders based on our dogList observable. Without any MobX, our component might look something like this:

const DogListView = () =>{ 
let result = dogStore.dogList.map((dog)=> <h1>{dog}</h1>);
  return (<div>
{result}
</div>);
}));

Now we need a way for our list component to listen & observe our DogStore class. Otherwise, how will it know to update when a new dog has been added?

MobX observable stores combined with Provider / inject / observer can make your components be truly ‘reactive’ by automatically rendering based on your data.

From MobX we can use Provider, inject, & observer to allow our DogListView component to watch our DogStore.

(1)
const DogListView = inject("dogStore")(observer(({dogStore}) =>{
  let result = dogStore.dogList.map((dog)=> <h1>{dog}</h1>);
  return (<div>
{result}
</div>);
}));
@observer  (2)
class App extends Component{
dogStore = new DogStore();
render(){
const dogStore = this.dogStore;
return(
 <Provider dogStore={dogStore}> (3)
<div>
<DogListView/> // inserting our DogListView
</div>
</Provider>
);}
}

Walking through the changes in order from labels 1–3:

(1)* using ‘inject’ and ‘observer’ we are adding a layer that allows our DogListView component to act as a stateless function and automatically update to any changes in ‘dogStore’ (*this implementation does not use decorators )

(2) the ‘@observer’ decorator lets our class App listen to events from our observable DogStore property dogList

(3) here ‘Provider’ is how we pass down the store from the App level to child components — any component in between the <Provider> tags will be able to access the stores listed in the first <Provider> tag

Now we can start to see MobX in action!

3. Changing how the app component renders based on the ‘view’ observable

Now that we have our list, we need a way for us to render the ‘Add Doggo’ view when we want to update our dogList store. One way we can do this is to add a button that when clicked will update our view to be ‘addDogView’.

This is pretty straightforward and can be done by adding a new line of code below in our App component:

@observer  
class App extends Component{
dogStore = new DogStore();
render(){
const dogStore = this.dogStore;
return(
<Provider dogStore={dogStore}> 
<div>
<DogListView/>
<button onClick={()=>dogStore.setView('addDogView')}>
Add Dog
</button>

</div>
</Provider>
);}
}

So now, when we click this button the state of ‘view’ will change from the default ‘listView’ to ‘addDogView’.

This means we need to create a new component to render for our ‘addDogView’ state. It can be a simple form with text input where we can add the dog’s name and then on submit event we update the dogList store.

const AddDogView = inject("dogStore")(observer(({dogStore}) =>{ 
function updateList(event){
const dogName = event.target.dogName.value;
dogStore.addDog(dogName);
dogStore.setView('listView');
}
return (<div>
<form onSubmit={(e)=>updateList(e)}>
<input type="text" id="dogName"/>
<input type="submit" value="Save"/>
</form>
</div>);
}));

Since this component will need access to the dogStore’s addDog method, we pass down the dogStore again using inject & observer.

Now we can add our new AddDogView component to our App, along with a nifty <If> component so that our App renders different views based on whether we’re currently looking at ‘listView’ or ‘addDogView’.

const If = ({ cond, children }) => cond ? children : null;
@observer
class App extends Component{
dogStore = new DogStore();
render(){
const dogStore = this.dogStore;
return(
<Provider dogStore={dogStore}>
<div>
<If cond={dogStore.view === 'listView'}>
<div>
<button onClick={()=>dogStore.setView('addDogView')}>
Add Dog
</button>
<DogListView/>
</div>
</If>
<If cond={dogStore.view === 'addDogView'}>
<div>
<AddDogView/>
</div>
</If>
</div>
</Provider>
);}
}

4. Voila!

With added CSS and some headers for each of our views, we now have a simple dog list that is dynamic and can take in user input!

Play around with it here: https://jsfiddle.net/xsd7upv8/14/

I also highly recommend this MobX demo & walk through if you want to learn more! https://swizec.com/blog/structure-mobx-app-real-world/swizec/7181


I hope this demo and walk through can help some of you learning MobX! I am by no means an expert, but I had a lot of fun playing around with it in my applications and I wanted to share how I got it to work. If this was handy in any way, sharing a clap or two would really mean a lot to me!

Thank you and good luck :)