React and State management

React and State management

If you are familiar with React and its state management or just want to read about Mobx as an alternative to redux then skip to Mobx and Redux Differences.

Out of the box React prompts state close to your functionality. React already comes with state management, you don’t need Mobx or Redux (well...not yet). React follows a OOP design pattern of having its state close to its functionality and then you encapsulate functionality with its state. A lot of devs say React is very functional and it can be, but out of the box it has a very objected orientated interface. Every component encapsulates its functionality and its state.

The only way to mutate the internal state of a component is by passing props to the component. Props are passed to the component and something happens internally, which updates the state of the component. That encapsulated state is why React “out of the box” is fully object oriented.

Things get interesting when you have two components and you are trying to sync state between them. If you don’t have a direct way to mutate state, then your solution is to lift the state up a level to a parent component. This parent keeps track of the state and passes its state as props to its children.

The problem is as your application grows, you keep moving state up and up and up and up. Now this creates a huge tree of components. So now our state management is coupled with our component tree. Good luck moving your components around or adding new components to that structure. A large complicated tree of components can also introduce performance issues when React reconciles changes.

Enter State Containers

One solution to the React state tree crazies is the state container pattern. So now our state is defined “somewhere else”. We can update and move components around the tree with less issues and our performance is better since we only render when something changes. Both Mobx and Redux are used to manage state in JavaScript applications and they follow the state container pattern.

Mobx and Redux Differences

In Redux you have middleware, reducers, sagas, and actions. Redux dispatches actions with a type, a payload, and maybe some meta data too. This action then goes through a pipeline of middleware and finally hitting a reducer the generates a new state, it does not mutate it. This has a lot of benefits one of them being easy undo and redo. You get a lot of control from beginning to end how your application works, it is predictable, and easy to reason about. Now this explicit control is also the con of redux, because everything is manual, you write everything out yourself. There is a lot of boilerplate or a lot of libraries to get over that boilerplate.

MobX has observers, actions, observables, and some other apis that we can use.  MobX achieves the same goals a little simpler. With MobX you dispatch an action, which then modifies your state. You create values and describe them as observables. An observable means when it updates everything that depends or Observers on it updates as well. MobX does all this action subscribing and dispatching for you and most of the time it will probably do it better than you would if you did it manually like in Redux.

MobX state containers can be on a component level or they can exist as a much larger global store like Redux or you can do both at the same time. In Redux you keep all your state in one global store or one global state. Multiple reducers allow you to alter the immutable state.

image3.png

A very basic example:

import {observable, computed, action} from 'mobx'
import api from '/services/api'
class DataStore {
  @observable loading = true
  @observable data = null
  @observable error = false
  @computed get isLoading() {
	return this.loading
  }
  @computed get hasError() {
	return this.error
  }
  @action startLoading() {
	this.loading = true
  }
  @action endLoading() {
	this.loading = false
  }
  @action setData(data) {
	this.data = data
  }
  @action setError(error) {
	this.error = error
  }
  @action fetchData(nrql) {
	this.startLoading()
	this.setError(false)
	
	api
  	.fetch(nrql)
  	.then((payload) => {
  	
    	if (payload) {
      	this.setData(payload)
    	} else {
      	this.setError(true)
    	}
  	
    	this.endLoading()
  	})
  	.catch((error) => {
    	this.setError(true)
    	this.endLoading()
  	})
  }
}
const dataStore = new DataStore()
//routes if we want to insert our data store through our provider.
<Provider dataStore={dataStore}>
	<Router history={history}>
// routes  	
	</Router>
  </Provider>
//here we register this component as a observer.
@observer
/*
Stores can be singeltons and injected onto our props of the component.
This is nice if we need to share the state of this store between multiple components or even the whole app.
*/
@inject('dataStore')
class DataExample extends Component {
  static propTypes = {
	nrql: PropTypes.string,
	className: PropTypes.string,
  }
  static defaultProps = {}
  constructor(props, context) {
	super(props, context);
	//  or we can create a new store for every instance of the component.
	// we could also import the store into this object as well.
	this.dataStore = new DataStore()
  }
  componentDidMount() {
	const {nrql} = this.props
	this.dataStore.fetchData(nrql)
  }
  componentWillReceiveProps(nextProps, nextContext) {
	const {nrql} = this.props
	if (nrql !== nextProps.nrql) {
  	this.dataStore.fetchData(nextProps.nrql)
	}
  }
  render() {
	const {className} = this.props
	
	if(this.dataStore.isLoading){
  	return(
    	Loading...
  	)
	}
	
	if(this.dataStore.hasError){
  	return(
    	we have error
  	)
	}
	
	return (
  	<div>
    	<span>this.dataStore.data</span>
  	</div>
	);
  }
}

Should your team use MobX?

Is your application derived data or computed? If your application relies heavily on derived data perhaps your dependencies would be much more straightforward and maintainable when described using MobX instead of Redux and its selectors.

More importantly, think about your team. Do you have team members familiar and comfortable with functional programming or does your team have more OOP experience? How fast can your team learn Redux/MobX/State Pattern? Smaller organizations and smaller teams of engineers are much more T shaped, having a code base that plays well to your team’s expertise can improve your overall velocity regardless of the state management solution you pick.

A more object oriented team might find that MobX works well for their use cases and their  learning curve can be shorter. MobX has a very small API footprint and requires minimal boilerplate. This makes it easy to onboard developers who aren't familiar with JavaScript and have them be productive quickly. The small boilerplate footprint creates code that is both explicit and simple to follow. Similar to react, MobX has the just right level of abstraction that allows for complex behavior while still letting the UI code be the focus.

Previous
Previous

Luma training thoughts

Next
Next

How the mobile APM UI handles state at New Relic