Codementor Events

Building a React App with Redux to Manage Data Flows

Published Jul 25, 2016Last updated Jan 18, 2017

What is React?

React is a JavaScript library used for creating interactive user interfaces. React is developed and maintained by Facebook as an open-source library. React documentation is comprehensive.

React has HTML, CSS, and JavaScript defined within components. HTML Layout is prescribed in a declarative style using JSX. CSS is defined using inline styles. The UI interactions are available as custom event handlers within React components.

Every React component maintains props and state. Props are passed to the component to configure the component. The state is used to change the way the component is rendered at run-time. A React component can change its state, but cannot change its props. The parent component containing the component can change the props. The component re-renders itself when the props or the state changes.

What is Redux?

React is a UI library. It is not interested in managing data flows. Redux is a library that manages data flow in a React application. Redux implements uni-directional data flow.

Actions, Store, and Reducers lie at the heart of Redux. Store is where the data resides. To update the store, React components emit actions. The store updates itself with the help of a reducer which can process the action. The updated store is available to the React components via props.

Overview of the sample application

The sample application that manages data flows we are going to build is a "Volunteer" application. Blood donors can volunteer by entering the name in the Add Donor form. The list of donors can be viewed in Donor display component. The JavaScript classes involved in the sample application are shown in the diagram below.

The top three classes, AddDonorAction, Store, and DonorReducer belongs to Redux. In a typical application, there will be many actions and reducers. However, Redux allows only a single store for the data.

The bottom four classes, AppContainer, App, DonorForm, and DonorDisplay, are React components. DonorForm has the Add Donor functionality. DonorDisplay displays a list of donors. App is the parent component for DonorForm and DonorDisplay. AppContainer has the plumbing logic for integrating React with Redux.

react-redux

Building the React components

Form component

Every React component has a render function. The render function is called multiple times whenever the props or state of the component changes. The render function of the DonorForm component is shown below:

  render() {
    return (
      <form className="form form-horizontal" onSubmit={this.handleSubmit}>
        <div className="form-group">
          <label htmlFor="name">Donor Name: </label>
          <input
            type="text"
            className="form-control"
            value={this.state.value}
            onChange={this.handleChange}
          />
        </div>
        <div className="form-group">
          <input
            type="submit"
            className="btn btn-primary"
            value="Submit"
          />
        </div>
      </form>
    );
  }

The component has a text box and a submit button. The text box is a controlled component. The handleChange event of the text box changes the state, and value.

The handleSubmit function passes on the submit functionality to the parent component, App.

Display component

The display component renders all the donors. The code for the render function for the display component is shown below.

  render() {
    return (
      <div>
        {this.props.volunteers.length > 0 ? (
          this.props.volunteers.map((v) => (
            <div key={v.name} className="bg-info text-info volunteer">
            {v.name}
            </div>
          ))
        ) : null}
      </div>
    );
  }

The list of volunteers (donors) is passed on to the display component from the App component (parent). The display component iterates over each volunteer and displays the name of the volunteer.

App component

The App component holds both the form component and the display component. The render function of the App component is shown below.

render() {
    return (
      <div>
        <div className="jumbotron">
          <h1>Volunteer</h1>
        </div>
        <div className="well">
          <DonorForm onSubmit={this.handleSubmit} />
        </div>
        <div className="panel panel-primary">
          <div className="panel-heading">Donors</div>
          <div className="panel-body">
            <PatientDisplay volunteers={this.props.donors} />
          </div>
        </div>
      </div>
    );
  }

The App component handles the submit function of the form component. The App component provides an array of donors to the display component.

Building the Redux functions

Redux can be installed using npm. Both Redux and React-Redux should be installed via npm.

npm i redux react-redux --save

Actions

Actions in Redux are functions which return an action object. The action object contains the action type and the action data. We want to add a donor to the system. To add a donor, we should define addDonorAction as follows.

export function addDonorAction(donor) {
  return {
    type: actionTypes.addDonor,
    donor,
  };
}

Reducers

Reducers in Redux are functions which update the state of the store. The store can be considered as a collection of reducers, each of which maintains its own state. To update the array of donors, we should define donorReducer as follows.

export default function donorReducer(state = [], action) {
  switch (action.type) {
    case actionTypes.addDonor:
      return [...state, action.donor];
    default:
      return state;
  }
}

The reducer receives an initial state and an action. Based on the action type, it returns a new state for the store. The state maintained by reducers are immutable. We return a new state object whenever the state changes.

Store

There is only one Store while using Redux. Store is configured via the createStore function.

export function configureStore(initialState) {
  return createStore(rootReducer, initialState);
}

The createStore function accepts a root reducer and an initial state. The initial state may come from a persistent store such as database. The root reducer is a collection of all reducers in the application.

const rootReducer = combineReducers({
  donors: donorReducer,
});

Integrating React with Redux

We have defined the React components. We have defined the Redux functions. The AppContainer is the container component which has the integration code to connect React components with Redux functions. To facilitate the integration, the React-Redux npm package is available.

The code for AppContainer is shown below.

const mapStateToProps = (state) => {
  return {
    donors: state.donors,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    onAddDonor: (donor) => dispatch(addDonorAction(donor)),
  };
};

const AppContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

The connect function in React-Redux creates the AppContainer component. AppContainer is a container component which has two additional props; donors and onAddDonor. These properties are attached with two helper functions, mapStateToProps and mapDispatchToProps.

The mapStateToProps function attaches the state of the store to the props. In our example, a new props called donors is attached. Whenever the React component uses donors, the state maintained by the corresponding reducer is returned.

The mapDispatchToProps function attaches a function to the props which dispatches an action to the store. In our example, the onAddDonor prop dispatches the corresponding action to the store. Dispatching an action to the store will invoke the corresponding reducer function, which will update the store data.

GitHub project

Redux introduces some complexity to a React application. Fortunately, the complexity is a one-time effort. There is a GitHub project available as a starter project. The starter project has the framework for managing data flows in a React application using Redux.

Discover and read more posts from Vijay Thirugnanam
get started
post comments1Reply
Александр Моисеенко
8 years ago

Great explanation. Thank you.