Become a Redux expert.
By: Krishna Kumar
Tags: React, Getting Started, Create React App, Beginner, Redux

It is an awesome feeling when you start using React for the first time - it is more or less Javascript, but with a few additional concepts that are easy to understand. You will be going good with React in no time. But, over time you’ll be hearing about the global store, Redux, MobX, etc. I was in the same place and wanted to learn Redux, so I started watching videos and reading articles and I couldn’t get it completely until I built a React application with Redux from scratch.

In this article, we will be jumping straight into Redux by creating a small application ‘Cricket Scoring App’. We won’t be creating all the features in this article but will be building very basic features to understand Redux. I will let you decide and build more features on top of this.

In this project, we will be building an app to keep the score of a cricket match Total runs, Wickets and number of overs elapsed. For each ball, we will track the result, whether it was a wicket, no ball, a wide or runs.

Let’s start the project by using create-react-app . Create a project cricket-scoring-app and build the UI something similar to the above picture.

Next step, we need to add Redux to this project. Actually, we need two packages redux & react-redux .

npm install -s redux react-redux

One of the main aims to use redux is, separating the state management logic to a commonplace than within the components. So all the components can directly access these states whenever they needed.

In order to manage the state outside from the React components, we need a place to keep the state . we’ll call it as a Store

So let’s go ahead and create the store.

Open cricket-scoring-app/src/App.js and add these lines.

import { createStore } from 'redux'; const myStore = createStore();

Now a store is created but it’s an empty store. We need to tell Redux’s createStore() function about the structure of our state . For that we need to create a function that will return the InitialState, let’s call this function Reducer.

So the above code becomes.

import { createStore } from 'redux'; function myReducer() {

return {

runs: 0,

balls: 0,

wickets: 0

};

} const myStore = createStore(myReducer);

Next, we need to pass this store to all our components, for that, we need to wrap our first (or) root (or) main component inside <Provider></Provider> component.

import { Provider } from 'react-redux';

….

…. render() {

<Provider store={myStore} >

<MainComponent />

</Provider>

}

Read values from Redux-Store

Even though the Redux-Store ie myStore is ready for all the child components of <MainComponent> , we need to explicitly wire the components which need values from the Redux-Store.

For that, we use the connect() function from react-redux .

In your MainComponent.js file, the export default will look like this

export default MainComponent;

Change this to

import { connect } from 'react-redux'; export default connect(MainComponent);

Now the MainComponent becomes a Connected-Component

All the components which are connected to the Redux-Store will get the values as props from the store .

So we need a function that will read the values from the store and converts it as props and passes it to the Connected-Component.

In common practice, we call this function mapStateToProps. As the name implies the function will read the state from the store and map it with the props for the component.

function mapStateToProps(state) {

return {

score: state.runs,

wickets: state.wicket,

balls: state.balls

}

}

Now we need to pass this mapStateToProp function to the connect function, so that we will get the state as the argument and it can return these values as props to the Component, MainComponent in the above case.

So we will rewrite

export default connect(MainComponent);

to

export default connect(mapStateToProps)(MainComponent);

The way we change the connect() function call to accommodate mapStateToProps may seem a little weird. This is because connect() is a HOC (Higher Order Component). In simple terms, HOC means the first call connect(mapStateToProps) will return a function , which we will call by passing MainComponent .

Now we are done with connecting all the parts. So in the render() function of MainComponent , we can show the score like this.

render() {

return(<div>{this.props.score}</div>);

// Here the `score` is from the `mapStateToProps` function.

}

Updating the State in Redux-Store

The way which we update the state is through the Reducer function, myReducer . We have explained earlier that the job of a reducer is to return the initialState . A slight change in that explanation is, the job of a reducer is to return a state .

So whatever be the state Reducer function returns, it will be kept in the Redux-Store. That makes the Reducer a better place for manipulating/updating the state .

Manipulation of state through Reducer function is done by Actions.

The reducer function will accept two arguments (initialState, actions) .

Our reducer function will change from

function myReducer() {

return {

runs: 0,

balls: 0,

wickets: 0

};

}

To

const initialState = {

runs: 0,

balls: 0,

wickets: 0

} function myReducer(state = initialState, action) {

return state;

}

Ok, now we need to modify the state based on the action;

When we click on these buttons we need to change the score accordingly.

That means we will Dispatch an Action when we click on these buttons and which will be handled by the reducer and updates the state .

For the reducer to distinguish each Action, the action argument of reducer function should have a type . Based on this type we will change the state .

function myReducer(state = initialState, action) {

switch(action.type) { case "INCREASE_SCORE_BY_ZERO":

return {...state, balls: state.balls + 1}; case "INCREASE_SCORE_BY_ONE":

return {

...state,

balls: state.balls + 1,

runs: state.runs + 1

}; case "INCREASE_SCORE_BY_TWO":

return {

...state,

balls: state.balls + 1,

runs: state.runs + 2

}; case "INCREASE_SCORE_BY_THREE":

return {

...state,

balls: state.balls + 1,

runs: state.runs + 3

}; case "INCREASE_SCORE_BY_FOUR":

return {

...state,

balls: state.balls + 1,

runs: state.runs + 4

}; case "INCREASE_SCORE_BY_SIX":

return {

...state,

balls: state.balls + 1,

runs: state.runs + 6

}; case "INCREASE_WICKET_LOST":

return {

...state,

balls: state.balls + 1,

wickets: state.wickets + 1

}; case "INCREASE_NO_BALL_SCORE":

return {...state, runs: state.runs + 1}; case "INCREASE_WIDE_BALL_SCORE":

return {...state, runs: state.runs + 1}; default:

return state;

}

}

We need a default case to return the initialState since during the page load we are not dispatching any action so action.type won’t be available and remember reducer should always return a state .

Since our reducer is capable of handling different actions, we need to find how we can dispatch these actions while we click on each of those buttons.

Again the magic happens at the connect() HOC function. Remember we created a mapStateToProps function to read state from store and map it to props ?

This time we will create another function mapDispathcToProps that will map actions to Props. These Actions are normal functions which will return the corresponding type .

function scoredZeroRun() {

return {type: 'INCREASE_SCORE_BY_ZERO'};

} function scoredOneRun() {

return {type: 'INCREASE_SCORE_BY_ONE'};

} function scoredTwoRuns() {

return {type: 'INCREASE_SCORE_BY_TWO'};

} function scoredThreeRuns() {

return {type: 'INCREASE_SCORE_BY_THREE'};

} function scoredFour() {

return {type: 'INCREASE_SCORE_BY_FOUR'};

} function scoredSix() {

return {type: 'INCREASE_SCORE_BY_SIX'};

} function wicketLost() {

return { type: "INCREASE_WICKET_LOST" };

} function noBall() {

return { type: "INCREASE_NO_BALL_SCORE" };

} function wideBall() {

return { type: "INCREASE_WIDE_BALL_SCORE" };

} const mapDispatchToProps = {

addZeroRun: scoredZeroRun,

addOneRun: scoredOneRun,

addTwoRuns: scoredTwoRuns,

addThreeRuns: scoredThreeRuns,

addFourRuns: scoredFour,

addSixRuns: scoredSix,

addWicketLost: wicketLost,

addNoBall: noBall,

addWideBall: wideBall

} export default connect(mapStateToProps, mapDispatchToProps)(MainComponent);

Our scorecard component render() will be updated to

render() {

// The `props.score`, `props.wickets` is from the `mapStateToProps` function.

// The `props.oneRun, props.twoRuns, etc` methods are from `mapDispatchToProps` function. return(

<div className="container text-center">

<div className="card">

<div className="score">

{this.props.score} / {this.props.wickets}

</div>

<div className="balls">

Balls: {this.props.balls}

</div>

</div> <div className="points-wrap">

<button className="point" onClick={this.props.addZeroRun}>

0

</button>

<button className="point" onClick={this.props.addOneRun}>

1

</button>

<button className="point" onClick={this.props.addTwoRuns}>

2

</button>

<button className="point" onClick={this.props.addThreeRuns}>

3

</button>

<button className="point" onClick={this.props.addFourRuns}>

4

</button>

<button className="point" onClick={this.props.addSixRuns}>

6

</button>

</div>

<div className="match-actions-wrap">

<button

className="match-actions wicket"

onClick={this.props.addWicketLost}

>

Wicket !

</button>

<button

className="match-actions no-ball"

onClick={this.props.addNoBall}

>

No Ball

</button>

<button

className="match-actions wide-ball"

onClick={this.props.addWideBall}

>

Wide

</button>

</div>

</div> }

So with this, we have completely wired redux with React Components.

We were introduced about 8 main concepts/terminologies at once while we learn redux.

Redux Store Reducer Provider Connect MapStateToProps MapDispatchToProps Actions

It will be a little difficult to get your head around all of these concepts in the first go itself. The easy way is Read, Write & Repeat.

You can find the complete code here: Github.

In the next article, I will explain a little more on advanced concepts or best practices using Redux, using the same example. It’s more of like a code refactoring using best practices, which will cover Container component and Presentational component, Combined reducers, action creator.