Skip to content

ReactJS and Redux introduction

Lorenzo Pini edited this page May 31, 2016 · 6 revisions

ReactJS

ReactJS 0.14.8 is used to develop MapStore2. The main purpose of ReactJS is to allow writing the View of the application, through the composition of small components, in a declarative way.

Components are written using a "templating" language, called JSX, that is a sort of composition of HTML and Javascript code. The difference between JSX and older approaches like JSP is that JSX templates are mixed with Javascript code inside javascript files.

ReactJS component example

Component definition:

var MyComponent = React.createClass({
   render: function() {
       return <h1>{this.props.title}</h1>;
   }
});

Component usage:

React.render(<MyComponent title="My title"/>, document.body);

Properties, State and Event handlers

Components can define and use properties, like the title one used in the example. These are immutable, and cannot be changed by component's code.

Components can also use state that can change. When the state changes, the component is updated (re-rendered) automatically.

var MyComponent = React.createClass({
   getInitialState: function() {
      return {
         title: 'CHANGE_ME'
      };
   },
   changeTitle: function() {
      this.setState({
         title: 'CHANGED'
      });
   },
   render: function() {
       return <h1 onClick={this.changeTitle}>{this.state.title}</h1>;
   }
});

In this example, the initial state includes a title property whose value is CHANGE_ME.

When the h1 element is clicked, the state is changed so that title becomes CHANGED.

The HTML page is automatically updated by ReactJS, each time the state changes (each time this.setState is called). For this reason we say that JSX allows to declaratively describe the View for each possible application state.

Lifecycle hooks

Components can re-define some lifecycle methods, to execute actions in certain moments of the component life.

var MyComponent = React.createClass({
   ...
   componentWillMount: function() {
       console.log('not mounted yet');
   },
   componentDidMount: function() {
       var domElement = React.findDOMNode(this);
       console.log('DOM mounted');
   },
   ...
});

Redux

Redux, and its companion react-redux are used to handle the application state and bind it to ReactJS components.

Redux promotes a unidirectional dataflow (inspired by the Flux architecture) and immutable state transformation using reducers, to achieve predictable and reproducable application behaviour.

A single, global, Store is delegated to contain all the application state.

The state can be changed dispatching Actions to the store.

Each action produces a new state (the state is never changed, a new state is produced and that is the new application state), through the usage of one or more reducers.

(Smart) Components can be connected to the store and be notified when the state changes, so that views are automatically updated.

Actions

In Redux, actions are actions descriptors, generated by an action creator. Actions descriptors are usually defined by an action type and a set of parameters that specify the action payload.

const CHANGE_TITLE= 'CHANGE_TITLE';

// action creator
function changeTitle(newTitle) {
    return {
        type: CHANGE_TITLE,
        title: newTitle
    };
}

Reducers

Reducers are functions that receive an action and the current state and:

  • produce a new state, for each recognized action
  • produce the current state for unrecognized actions
  • produce initial state, if the current state is undefined
function reducer(state = {title: "CHANGE_ME"}, action) {
    switch (action.type) {
        case CHANGE_TITLE:
            return {title: action.title};
        default:
            return state;
    }
}

Store

The redux store combines different reducers to produce a global state, with a slice for each used reducer.

var rootReducer = combineReducers({
   slice1: reducer1,
   slice2: reducer2
});
var initialState = {slice1: {}, slice2: {}};

var store = createStore(rootReducer, initialState);

The Redux store receives actions, through a dispatch method, and creates a new application state, using the configured reducers.

store.dispatch(changeTitle('New title'));

You can subscribe to the store, to be notified whenever the state changes.

store.subscribe(function handleChange() {});

Redux and ReactJS integration

The react-redux library can be used to connect the Redux application state to ReactJS components.

This can be done in the following way:

  • wrap the ReactJS root component with the react-redux Provider component, to bind the Redux store to the ReactJS view
React.render(
    <Provider store={store}>
        {() => <App />}
    </Provider>,
    document.getElementById('container')
);
  • explicitly connect one or more (smart) components to a all or part of the state (you can also transform the state and have computed properties)
connect(function(state) {
    return {
        title: state.title,
        name: state.namespace + '.' + state.name,
    };
})(App);

The connected component will get automatic access to the configured slice through properties:

function render() {
   return <div><h1>{this.props.title}</h1><p>{this.props.name}</p></div);
}