React & Redux: a perfect combination for big projects

With the increasing complexity of web applications, the responsibility of updating and outputting their basic data becomes more complex. Various ways of controlling these data lead to a compound web representation. These views can listen for updates to various models that send their adjustments to even more views. All this leaves programmers with an opaque and unpredictable code that is almost unreal to edit without forgetting about any important trifles.

Even a worse thing is that the developer can add a bug into another element that is not related to this element or element in the program. It’s high time to introduce Redux as a state container for a program in JavaScript offering a solution to this problem.

Redux is a super great tool for handling UI-state and data-state in JS apps. It’s a perfect solution for developing single page programs in which it will be comfortable to maintain the state over time. You can use it not only with React but also with jQuery or Angular apps.

Let’s get to the point right away. React is passing data via elements. You may find that people call it as a unidirectional data stream. We have a standard data flow from a parent to a child. There’s one concern here regarding the other types of elements and their communication.

A direct element-to-element communication is not as good as you may end up with a spaghetti code. There’s a great solution provided by Redux for storing program state in one location. It’s the store. If there’s a need for some elements to send changes, they do it via the store. If there’s a need for some elements to know all the changes, they subscribe to it.

Let’s take a look how these two ways of communication between the elements look like:

The presence of the store makes data flow easier. All the elements know their current states and they are aware of the changes in the other elements. Flux pattern is used for supplementing data flow constructions. One of such is React. How can we combine Redux and Flux?

Flux and Redux

Flux has lead to the creation of Redux. They are not the same thing although. Redux is a tool while Flux is a pattern. Here are the three fundamentals of Redux that makes it different from Flux:

  1. One and only Source of Truth. It’s the store that keeps the state in one location;
  2. Read-only state. You should send an action to make adjustments to the state;
  3. Pure Functions make changes. Reducers handle the sent actions and they lead to inevitable changes.

Creating the Store

Use Redux.createStore() and add reducers functions as arguments. Here’s an example of how it can look like:

What has just happened? We’ve made a store that has one reducer. It defines the primary state of the app as an empty array. This user is added to the state with the reducer. The store gets updated.

Redux starts reducers and gets their return values right after the store is designed. Redux uses these values as the initial state. Undefined is passed to the reducer following the first call. The reducer code predicts this and gives back an empty array. This handles the initial store state.

We can observe the reducers to be called in case any action is sent. Redux considers handling states in return. If we consider the example, we’ll see that the dispatch comes first and the next call to the reducer comes after it. There’s an action object “ADD_USER” that lets the reducer be aware of how the state can be changed.

You can consider reducers to be a tube and the state goes through it:

We will have one single user object with an array as a store:

Duplicate the state but don’t change it

We have a reducer that changes the state and this is wrong. We should make sure that all the arguments that get to the reducer are immutable. Don’t worry, you can use .concat() as a non-mutating method. In short, we create a copy of the array, make changes and give back the copy of it:

When we have a new user added, we can see how the copy of the state argument is changed. And we get the primary state if no users were added.

Various reducers

You have seen a simple example of the code but let’s go deeper into a complex state. As you know, Redux has only one store for the whole application. Our goal is to divide the state into various sections with the help of nested objects. Let’s consider having a store that looks like the following:

We still have one store as one object. However, there are already nested objects for widgetState and userState that may keep any data type. This example is as easy as a pie but it has something in common with a real Redux store.

Here we determine a reducer for every section. It will let us make nested objects:

combineReducers() gives you a chance to add sections to the store and determine one reducer for one section. Once reducer gives back the primary state, this very state will flow into the widgetState or userState sections. Every reducer passes the corresponding subsection to the general state and not the whole store state as you have seen in the sample with one reducer.

What happens after the dispatch?

All the reducers are called in this case. Do you remember how we compared reducers to tubes? Reducers are called right after the action is sent. They are able to update their corresponding state:

We’d like to remind you the fact that every reducer doesn’t pass the whole state but only its corresponding state.

Immutable data structures

No matter what kind of state you have, you shouldn’t mutate the state object. You will need to give back a new object in case the state gets some changes. There are two key points that you should always keep in mind:

  • All JS simple data types are always permanent;
  • Functions, arrays and objects are changeable;

There’s a belief that data structures mutability lead to bugs. As we’re going to use state arrays and objects, we’ll develop a plan to maintain the permanent state. Let’s consider having a state object that we need to make adjustments with. Here’s how we can do it:

The state object is mutated in the samples. In the second case, we have Object.assign() sending its arguments to the first one. This is the reason for the third sample not to adjust the state. Take a look at the third sample. You will see how the state and {foo: 123} contents are merged to a new empty object. We’ve made the state copy, changed it without changing the primary state.

Use “spread operator” to maintain the permanent state:

Use different libraries. It’s very important to get the idea of immutability concept to create successful projects with Redux.

Primary state

createStore() stands for the so-called initial state. It doesn’t create the primary state it serves for the state hydration. What do you think of a situation when a user reloads the program and the state of the store is set to the primary one? That’s not so good. What if you could keep the store and re-hydrate it into Redux while the reload? The primary state is sent to createStore().

You can observe something similar to a time journey inside the application by rehydrating the old state. One of the most commonly used features is Undo and Redo. It’s handy to have all the state kept in one single store.

React and Redux

Let’s take a container element and add some Redux to it. Here’s the original element with no Redux:

This element sends the Ajax inquiry and adjusts its state. Such an approach won’t fit if the adjustments need to be made according to the collected user list. We have another situation with the use of Redux philosophy. We send an action after which receive the Ajax inquiry in return. We allow the element to subscribe to the state adjustments.

React-redux bindings

We’d like to admit that react-redux, redux and react are three different npm modules. You can implement a react-redux module for uniting React elements with Redux. Here’s what we get:

connect function is taken from react-redux. It has two arguments but we have displayed only one of them for mapStateToProps(). It has to send back an object. Two pairs of parentheses stand for two function calls.

The name appeared after combining reducers:

Action dispatching

react-redux comes to help when we require action dispatching:

There’s no more need to write the event.

The omission of the container element

We can craft container elements that don’t require any changes or methods but a subscription to the store only. However, there’s still one render() method that it may require for sending data to the presentational elements. Here’s what we get:

There’s no React.createClass(). We use connect() to make a container element. As you see, everything flows directly into the presentational element without making a separate container element.

Container elements serve to let presentational elements deal with the view and not the state. connect() sends state through the props into the representational elements and sends back the React element that keeps the presentational element.

Let’s consider container elements performing opposite functions:

Provider element

This element will wrap the whole React application, so make sure it knows how to use react-redux. Here’s what we might get with the React Router:

The store is added to the Provider for uniting Redux and React with the help of react-redux.

Conclusion

There’s a lot more to say about Redux. We’ve covered the essential issues that will fit developing Single Page Applications. Using this tool will allow developers scale applications in the future with no stress. For those who only choose the flux-architecture or have not worked with React at all, I want to say that the Redux approach is very appealing. It is a well-structured code and it’s readable. Even if you open an old project, you do not need a lot of time to remember how it works.