Redux setup

Let's start with a UserList example in Redux. First, create a directory with the application. We are using the Node.js server and npm package for this example because the Redux module is not available independently.

Installing Node.js

First, we have to download and install Node.js, if we have not already installed it in the system. We can download Node.js from http://nodejs.org . It includes the npm package manager.

Once the setup is done, we can check whether Node.js was set up properly or not. Open the command prompt window and run the following command:

node --version

You should be able to see the version information, which ensures that the installation was successful.

Setting up the application

First we need to create a package.json file for our project which includes the project information and dependencies. Now, open the command prompt/console and navigate to the directory you have created. Run the following command:

Npm init

This command will initialize our app and ask several questions to create a JSON file named package.json. The utility will ask questions about the project name, description, entry point, version, author name, dependencies, license information, and so on. Once the command is executed, it will generate a package.json file in the root directory of your project:

{ 
  "name": "react-redux add ticket form example", 
  "version": "1.0.0", 
  "description": "", 
  "scripts": { 
    "start": "node server.js", 
    "lint": "eslintsrc" 
  }, 
  "keywords": [ 
    "react", 
   "redux", 
   "redux form", 
    "reactjs", 
    "hot", 
    "reload", 
    "live", 
    "webpack" 
  ], 
  "author": "Harmeet Singh <[email protected]>", 
  "license": "MiIT", 
  "devDependencies": { 
    "babel-core": "^5.8.3", 
    "babel-eslint": "^4.0.5", 
    "babel-loader": "^5.3.2", 
    "css-loader": "^0.15.6", 
    "cssnext-loader": "^1.0.1", 
    "eslint": "^0.24.1", 
    "eslint-plugin-react": "^3.1.0", 
    "extract-text-webpack-plugin": "^0.8.2", 
    "html-webpack-plugin": "^1.6.1", 
    "react-hot-loader": "^1.2.7", 
    "redux-devtools": "^1.0.2", 
    "style-loader": "^0.12.3", 
    "webpack": "^1.9.6", 
    "webpack-dev-server": "^1.8.2" 
  }, 
  "dependencies": { 
    "classnames": "^2.1.3", 
    "lodash": "^3.10.1", 
    "react": "^0.13.0", 
    "react-redux": "^0.2.2", 
    "redux": "^1.0.0-rc" 
  } 
} 

OK, let me explain to you some of the major tools before we start:

  • webpack-dev-server: This is a server for live reload of our application.
  • babel-loader: This is the compiler for our JavaScript.
  • redux-devtools: This is a powerful tool for Redux development. Using this tool in development will help us to monitor the updates in the DOM UI.
  • classnames: This is amodule that will help us to apply the classes on condition.
  • eslint: This is a tool similar to JSHint and JSLint for parsing the JavaScript.

Development tool setup

First, we need to create webpack.config.js and add the following code to enable the redux-devtools:

var path = require('path'); 
varwebpack = require('webpack'); 
varExtractTextPlugin = require('extract-text-webpack-plugin'); 
vardevFlagPlugin = new webpack.DefinePlugin({ 
  __DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'true')) 
}); 
 
module.exports = { 
  devtool: 'eval', 
  entry: [ 
    'webpack-dev-server/client?http://localhost:3000', 
    'webpack/hot/only-dev-server', 
    './src/index' 
  ], 
  output: { 
    path: path.join(__dirname, 'dist'), 
    filename: 'bundle.js', 
    publicPath: '/static/' 
  }, 
  plugins: [ 
    new webpack.HotModuleReplacementPlugin(), 
    new webpack.NoErrorsPlugin(), 
    devFlagPlugin, 
    new ExtractTextPlugin('app.css') 
  ], 
  module: { 
    loaders: [ 
      { 
        test: /.jsx?$/, 
        loaders: ['react-hot', 'babel'], 
        include: path.join(__dirname, 'src') 
      }, 
      { test: /.css$/, loader: ExtractTextPlugin.extract
      ('css-loader?module!cssnext-loader') } 
    ] 
  }, 
  resolve: { 
    extensions: ['', '.js', '.json'] 
  } 
}; 

Now, create a directory with the name of src. Inside this we need to create some folders, as shown in the following screenshot:

Development tool setup

Redux application setup

In every Redux application, we have actions, reducers, stores, and components. Let's start with creating some actions for our application.

Actions

Actions are the part of the information that sends data from our application to our store.

First we need to create the UsersActions.js file inside the actions folder and put the following code inside it:

import * as types from '../constants/ActionTypes'; 
 
export function addUser(name) { 
  return { 
    type: types.ADD_USER, 
    name 
  }; 
} 
 
export function deleteUser(id) { 
  return { 
    type: types.DELETE_USER, 
    id 
  }; 
} 

In the preceding code, we created two actions: addUser and deleteUser. Now we need to create ActionTypes.js inside the constants folder that defines the type:

export constADD_USER = 'ADD_USER'; 
export constDELETE_USER = 'DELETE_USER';  

Reducers

Reducers handle the actions which describe the fact that something happened, but managing the state of the application is the responsibility of the reducers. They store the previous state and action and return the next state:

export default function users(state = initialState, action) { 
  switch (action.type) { 
    case types.ADD_USER: 
    constnewId = state.users[state.users.length-1] + 1; 
      return { 
        ...state, 
        users: state.users.concat(newId), 
        usersById: { 
          ...state.usersById, 
          [newId]: { 
            id: newId, 
            name: action.name 
          } 
        }, 
      } 
 
     case types.DELETE_USER: 
     return { 
       ...state, 
       users: state.users.filter(id => id !== action.id), 
       usersById: omit(state.usersById, action.id) 
     } 
 
     default: 
     return state; 
  } 
} 

Store

We have defined the actions and reducers that represent the facts about what happened and when we need to update the state according to those actions.

The store is the object that combines the actions and reducers. The store has the following responsibilities:

  • Holds the application state
  • Allows access and updates the state through getState() and dispatch (action)
  • Registers and unregisters listeners through subscribe (listener)

Here is the code of UserListApp.js inside the container folder:

constinitialState = { 
  users: [1, 2, 3], 
  usersById: { 
    1: { 
      id: 1, 
      name: 'Harmeet Singh' 
    }, 
    2: { 
      id: 2, 
      name: 'Mehul Bhatt' 
    }, 
    3: { 
      id: 3, 
      name: 'NayanJyotiTalukdar' 
    } 
  } 
}; 
import React, { Component, PropTypes } from 'react'; 
import { bindActionCreators } from 'redux'; 
import { connect } from 'react-redux'; 
 
import * as UsersActions from '../actions/UsersActions'; 
import { UserList, AddUserInput } from '../components'; 
 
@connect(state => ({ 
userlist: state.userlist 
})) 
export default class UserListApp extends Component { 
 
  static propTypes = { 
    usersById: PropTypes.object.isRequired, 
    dispatch: PropTypes.func.isRequired 
  } 
 
  render () { 
    const { userlist: { usersById }, dispatch } = this.props; 
    const actions = bindActionCreators(UsersActions, dispatch); 
 
    return ( 
      <div> 
        <h1>UserList</h1> 
        <AddUserInputaddUser={actions.addUser} /> 
        <UserList users={usersById} actions={actions} /> 
      </div> 
    ); 
  } 
} 

In the preceding code, we are initializing the state of the component with the static JSON data of UserList and using the getstate, dispatch (action), and we will update the store information.

Tip

We'll only have a single store in a Redux application. When we need to split our data handling logic, we'll use the reducer composition instead of multiple stores.

Components

These are the normal React JSX components, so we don't need to go into detail about them. We have added some functional stateless components that we'll use unless we need to use local state or the life cycle methods:

In this (AddUserInput.js) file, we are creating a JSX input component from where we take the user input:

export default class AddUserInput extends Component { 
  static propTypes = { 
    addUser: PropTypes.func.isRequired 
  } 
 
  render () { 
    return ( 
      <input 
      type="text" 
      autoFocus="true" 
      className={classnames('form-control')} 
        placeholder="Type the name of the user to add" 
        value={this.state.name} 
        onChange={this.handleChange.bind(this)} 
        onKeyDown={this.handleSubmit.bind(this)} /> 
    ); 
  } 
 
  constructor (props, context) { 
    super(props, context); 
      this.state = { 
        name: this.props.name || '', 
      }; 
  } 
} 

In UserList.js we are creating a list component where we iterate the value of the Input component:

export default class UserList extends Component { 
  static propTypes = { 
    users: PropTypes.object.isRequired, 
    actions: PropTypes.object.isRequired 
  } 
 
  render () { 
    return ( 
      <div className="media"> 
        { 
          mapValues(this.props.users, (users) => { 
            return (<UsersListItem 
              key={users.id} 
              id={users.id} 
              name={users.name} 
               src={users.src} 
              {...this.props.actions} />); 
          }) 
        } 
      </div> 
    ); 
  } 
}

After iterating the value in the UserList component, we are displaying that list in the Bootstrap media layout:

export default class UserListItem extends Component { 
  static propTypes = { 
    id: PropTypes.number.isRequired, 
    name: PropTypes.string.isRequired, 
    onTrashClick: PropTypes.func.isRequired 
  } 
 
  render () { 
    return ( 
      <div> 
      <div className="clearfix"> 
            <a href="#" className="pull-left"> 
            <img className="media-object img-thumbnail" 
            src={"http://placehold.it/64x64"}/> 
            </a> 
            <div className={`media-body ${styles.paddng10}`}> 
                  <h3className="media-heading"> 
                  <strong><a href="#">{this.props.name}</a></strong> 
                  </h3> 
            <p> 
                  Loremipsum dolor sit amet, consecteturadipiscingelit. 
                  Praesentgravidaeuismod ligula,
                  vel semper nuncblandit sit amet.  
            </p> 
 
            <div className={`pull-right ${styles.userActions}`}> 
            <button className={`btnbtn-default ${styles.btnAction}`} 
            onClick={()=>this.props.deleteUser(this.props.id)} 
            > 
            Delete the user <iclassName="fafa-trash" /> 
            </button> 
            </div> 
          </div> 
        </div> 
      </div> 
    ); 
  } 
}  

Now we need to wrap our components in UserListApp.js inside the container folder:

import { UserList, AddUserInput } from '../components'; 
@connect(state => ({ 
  userlist: state.userlist 
})) 
export default class UserListApp extends Component {  
  static propTypes = { 
    usersById: PropTypes.object.isRequired, 
    dispatch: PropTypes.func.isRequired 
  } 
 
  render () { 
    const { userlist: { usersById }, dispatch } = this.props; 
    const actions = bindActionCreators(UsersActions, dispatch); 
 
    return ( 
      <div> 
        <h1>UserList</h1> 
        <AddUserInput addUser={actions.addUser} /> 
        <UserList users={usersById} actions={actions} /> 
      </div> 
    ); 
  } 
}

Now, let's wrap this UserListApp component to the Redux store in App.js inside the container folder:

import UserListApp from './UserListApp'; 
import * as reducers from '../reducers'; 
 
const reducer = combineReducers(reducers); 
const store = createStore(reducer); 
 
export default class App extends Component { 
  render() { 
    return ( 
      <div> 
        <Provider store={store}> 
          {() => <UserListApp /> } 
        </Provider> 
 
        {renderDevTools(store)} 
      </div> 
    ); 
  } 
} 

Now go to the root directory, open the CMD, and run the following command:

To install the packages that we need for this app, run the following command:

Npm install

Once it's complete, run the following command:

Npm start

Observe the following screenshot:

Components

That looks amazing. In the right-side panel is the Redux DevTool which gives the update of the UI. We can easily see the updates for deleting or adding the user in this list.

The following screenshot shows the deletion of a user from UserList:

Components

The following screenshot shows the addition of a user:

Components

Note

Please see the source code for Chapter 6, Redux Architecture to get a proper understanding about the flow of the application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset