Introduction to React JS and Redux

Date 9 juin 2016 Cat├ęgories Developpement par VulgaireDev

I began to use ReactJS some weeks ago. Like I said in a previous post (in french), React is not a framework, but a library, its aim is not to provide tools for building a complete app : people say that it is the "V" in MVC. It does it really well, but what other tools can we use to build a complete app ? Well, we have Redux.

In this post, I'm going to explain the basics of Redux, and I will try to make it simple. It is just an introduction made for beginners.

Let's say I have a small application, where I have a Menu, with links, and a a content. When I click on links, i want my content to change (NB: we could do that with react-router but it is a simple example to illustrate).

A first approach could be to put our datas in App, and give a callback to children (via props) so that they can change the datas.

export default class App extends React.Component{
  constructor(){
    super();
    this.state = {
      currentReading: "This is the initial Reading"
    };
  }

  changeCurrentReading = (newReading) => {
    this.setState({currentReading: newReading});
  }

  render() {
    return (
      <div>
        <Menu changeCurrentReading={this.changeCurrentReading}/> {/* we give the callback for the children */}
        <Content content={this.state.currentReading}/>
      </div>
    )
  }
}

class Menu extends React.Component{
  render() {
    return (
      <div id="myMenu">
        <ul>
          {/*we need to put arrows functions here because onClick is waiting for a function */}
          <li><a onClick={() => this.props.changeCurrentReading("Lorem ipsum")}>Link 1</a></li>
          <li><a onClick={() => this.props.changeCurrentReading("Dolor sit amet")}>Link 2</a></li>
          <li><a onClick={() => this.props.changeCurrentReading("Consectetur")}>Link 3</a></li>
        </ul>
      </div>
    )
  }
}

class Content extends React.Component{
  render() {
    return (
      <div>
        <p>
          {this.props.content}
        </p>
      </div>
    )
  }
}
A schema of our app

It works. Now, imagine you have nested components : you would like to give the callbacks to children recursively. Imagine you have severals callbacks (or as we will name them, actions), many components who must emit differents actions, it is going to be quite difficult to maintain.

Maintainability could become an issue

 

To solve this problem, Facebook (creator of React) recommands using Flux. Flux is not a library, it is more of a pattern, a general architecture for your application, that's why there are many different implementations of Flux : Reflux, Fluxxuor, Alt ... And the most popular one, by far, is Redux, created by Dan Abramov. He presented his work during the react-europe convention in 2015 (check out his talk it's very interesting !), and the community started to use it very quickly.

So before anything, what exactly is Flux?

What the Flux?

First, it is not MVC. Well there are similarities, but try to think just like if you were a beginner who desn't know at all about MVC.

Facebook's schema of Flux

 

Your application emits Actions. Those actions are dispatched, and change the Store, which contains your datas (like a model). When the Store changes, it emits notification to the View, which reupdate itself (this last part is basically an Observer pattern).

It is a one-way data-binding (which you can differentiate from the two-way data-binding in AngularJs ), because the view updates itself when the store change, and not the opposite (in fact you can, but you will have to code it). The only way to change the store is to dispatch an action, and you are the only one that define what an action should do, so it is a very controlled system.

If we schematize our application using flux, it would more likely be something like that :

We have a single store, which contains the current datas of the application.

Redux as an implementation of Flux

Redux has its particularities.

First, we don't need a dispatcher element:

 

How Redux works

 

Secondly, with Redux we emit Actions, whiches change the Store via Reducers. When the store is changed, the View update itself.

Reducer is a key concept of Redux, it is a function who takes a state and an action, and create a new action.

If we schematize, the Redux's store could be something like that :

 

NB: Each time a reducer is called, a new State is generated. State object is immutable, and therefore it is impossible to modify it (this approach comes from functional paradigm). It is actually interesting for performances, because it's easy to know if the state has changed just by looking at its id, there is no need to check all nested properties of this object !

Let's code

Now that we have seen the idea of Redux, let's pratice !

First you need to install these dependencies :

npm install --save redux react-redux

So let's write a minimal code which will be able to make my content change when I click on links, using Redux.

We define our actions :

//actionIndex.js

export setCurrentReading = (currentReading) => {
  return {
    type: 'SET_CURRENT_READING',
    currentReading: currentReading
  }
}

Then, we create our reducer (wich describes how our store will change with this action):

//reducer.js

const reducer = (state, action) => {
    switch (action.type) {
        case 'SET_CURRENT_READING':
            {/*this function let us create a new object with the previous state (like a merge). For our simple case here we could just have returned {currentReading: action.currentReading}*/}
            return Object.assign({}, state, { 
                currentReading: action.currentReading
              })
        default:
            return state
    }
}

export default reducer

So the reducer defines what the store is composed of when an action is emitted, depending of the action (that's why there is a switch ... case). I insist on this point it's important.

Now, we have to connect things with React, it's quite simple. First, we have to create the store when we create the application.

//app.js

import React from "react"
import ReactDOM from "react-dom";
import { Provider } from 'react-redux';
import { createStore } from 'redux';

import App from "./components/app.js";
import reducer  from "./reducers/reducer.js";

const initialState = {
  currentReading: "This is the initial reading"
};

{/*We create the store with the initial state*/}
let store = createStore(reducer, initialState);

ReactDOM.render(
  <Provider store={store}>
    <App/>
  </Provider>,
  document.getElementsByClassName('generalContainer')[0]
);

The <Provider></Provider> gives acces to the store to all children components. More precisely, it makes the "connect" function accessible to children, because you have to specify wich component you want to connect to the store.

The "connect" function can takes several arguments, we will just look at the first one. It defines how to map the datas of the current state to the props of the current component. This function is called each time the store changes, so that props of the component is a reflect of the store.

//content.js
class Content extends React.Component{
  render() {
    return (
      <div>
        <p>
          {this.props.currentReading}
        </p>
      </div>
    )
  }
}
const mapStateToPropsContent = (state) => ({
    currentReading: state.currentReading
})
const ContentConnected = connect(mapStateToPropsContent)(Content);

NB important: You have to use the connected component now (<ContentConnected/> instead of <Content/>) !

Everything is now ok, we just have to dispatch an action in our component when the user clicks on the link :

import * as actions from actionsIndex.js
import React from 'react';
import { connect } from 'react-redux';

export default class App extends React.Component{

  render() {
    return (
      <div>
        <MenuConnected/>
        <ContentConnected />
      </div>
    )
  }
}

class Menu extends React.Component{
  render() {
    return (
      <div id="myMenu">
        <ul>
          {/*Here we dispatch our action*/}
          <li><a onClick={() => this.props.dispatch(actions.setCurrentReading("First reading"))}>Link 1</a></li>
          <li><a onClick={() => this.props.dispatch(actions.setCurrentReading("Second reading"))}>Link 2</a></li>
          <li><a onClick={() => this.props.dispatch(actions.setCurrentReading("Third reading"))}>Link 3</a></li>
        </ul>
      </div>
    )
  }
}
{/*We need to connect the Menu to access the "dispatch" prop"*/}
const MenuConnected = connect()(Menu);

class Content extends React.Component{
  render() {
    return (
      <div>
        <p>
          {this.props.content}
        </p>
      </div>
    )
  }
}
const mapStateToPropsContent = (state) => ({
    currentPath: state.currentPath
})
const ContentConnected = connect(mapStateToPropsContent)(Content);

It now works !

For the organization of our app, I suggest something like that :

npm_modules/

src/

    actions/

        actionsIndex.js

    reducers/

        reducers.js

    components/

        app.js

        content.js

        menu.js

index.html

webpack.config.js

package.json

 

This is a simple organisation, maybe you should consider, in a second time, making a distinction between container and components.

 

Here we are, we have a (very) simple application using React and Redux. It might seems a little bit of overkill in this case, but with a bigger app it clearly has its benefits, try it you'll see !

In addition, I made a boilerplate for React + Redux + Semantic UI. It is pretty useful to me : https://github.com/Romathonat/RRSUI-boilerplate

 

Links:

https://github.com/reactjs/react-redux/blob/master/docs/api.md#provider-store

http://redux.js.org/docs/introduction/ThreePrinciples.html

https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.9lmyzkil5

Commentaires

blog comments powered by Disqus