You might not need a sophisticated state management

tl;dr

This article describes how I use React’s out of the box state management mechanism instead of using sophisticated state management libraries and at the same time keep my code base maintainable.

The biggest mistake in the process of learning React

When I started learning React, my biggest mistake was to learn react and redux together.
Redux is a brilliant piece of software. But while having a very simple API, it is based on some complex ideas in software engineering like CQRS, event sourcing, immutability and Flux. On the other hand, Redux is very verbose. In order to implement a single feature, one should implement actions, action creators, reducers and probably selectors. Moreover, a real world Redux application forces you to deal with some other new concepts like normalized data, thunks or sagas, immutable data structures and memoization.
In addition, there are lots of state management libraries for React and each of them has different approach to solve this problem. I think a good programmer should find the best match which is not necessarily Redux.
Last but not least, you will not understand the necessity of state management unless you start working with React without a state management library.

To make it short:

DO NOT LEARN REACT AND REDUX AT THE SAME TIME!

In this article, I try to walk through the transition between the React internal state to the simplest state management solution. For many of the cases, this approach is OK. But for more complex applications, you may need to learn a sophisticated state manager like Redux.

The proposed solution in this article does not need any special knowledge other that React.

Why do we need state management?

To make it more accurate, “Why we need a separate state management?”. React ships with an out of the box state management mechanism for components. But there are two important reasons behind using a separate state management mechanism:

  1. Shared State: When we need to share state (eg. current time) between multiple components, handling it in a top level component’s state and wiring it to other components is not fun.
  2. Decoupling: State management logic is the connection point between business logic and presentation logic in a React application. If you manage to decouple state management logic from a component’s code, it is much easier to decouple business logic from presentation layer.

State Management in React

React’s documentation describes component’s internal state much better than me. If you are not familiar with it, navigate to this page.

Consider this Clock component which is exactly copied from React documentation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}

componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}

componentWillUnmount() {
clearInterval(this.timerID);
}

tick() {
this.setState({
date: new Date()
});
}

render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

Let extract the state management logic from Clock component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ClockStore {
constructor() {
super()
this.state = { date: new Date() }

this.timerID = setInterval(
() => this.tick(),
1000
);
}

tick() {
this.setState({
date: new Date()
});
}

destroy() {
clearInterval(this.timerID);
}
}

When you run this.setState in a React component, it will automatically start the rendering process and if the component needs to be rendered again, it will happen. We can replace this mechanism with a simple pub/sub system that tells the listeners of this state manager to update themselves. Let’s create a simple pub/sub mechanism that will be triggered by setState:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Store {
constructor() {
this.subscribers = [];
this.state = {};
}

setState(state) {
this.state = Object.assign({}, this.state, state);
this.dispatch();
}

subscribe(listener) {
this.subscribers.push(listener);

let isSubscribed = true;

return () => {
if (!isSubscribed) {
return;
}

isSubscribed = false;

const index = this.subscribers.indexOf(listener);

this.subscribers.splice(index, 1);
};
}

dispatch() {
this.subscribers.forEach(listener => listener());
}
}

Now if we simply subclass our ClockStore from Store, we have a fully functioning state manager! You can run this piece of code in node REPL as a working example.

Binding it to React

This store is almost useless unless you can bind it to a React component. The component should be informed when something changed inside the state. The idea is roughly the connect HOC from Redux project:

  1. Implement a dumb component which is unaware of state management
  2. Use a Higher Order Component called connect to bind needed stores to component’s properties. You may have multiple stores for different purposes.
  3. Implement a function called mapStoresToProps to teach connect how to bind stores to your component and pass it to connect
  4. Now you have a smart (connected) component!

The idea is implemented in a library called ReaStore. There is a step by step walkthrough in the library documentation.

Why?

As a React beginner or as someone who is working on early stages of a React project, there are a couple of reasons why you may want to use ReaStore:

  1. Learning: You shouldn’t learn React and Redux together (as mentioned before). As a learning path you should first write an application without any state management, then implement yourself a simple state manager like ReaStore, then understand Flux concept and then consider learning Redux or other state management solutions.
  2. Simplicity: This state management only needs five minutes to learn while it is powerful.
  3. Type Friendly: If you use Typescript or Flow, you can have type checking and intellisense for free. You don’t need to define any extra interfaces for your stores.

Enjoy Reacting!