Getting started with React-Redux

You have finally decided that you are going to use React for your application and you are all pumped up to start development until that fateful moment when you realize that you need data from a different component & the complexity of passing that data through props is making your code & your life HELL.

Here comes Redux to your rescue!!

In simple terms, Redux acts like a container to store the state of your javascript application which is accessible across all your components.

But why would you need such a thing?

Imagine this, you have your item listing application & for each item a user can mark it as favourite which would not only update your list view but also a total favourites list in your header.

You might be thinking that’s pretty easy, I can just pass the favourite list as props and update the element. But what if that component is deeply nested in your application which makes passing props through parent child component extremely ugly & complex or worse where you can’t pass a prop to the component–for instance Route Components like this:

// ProductView component is rendered when custom product id route is navigated
&ltRoute path="/product/:id" component={ProductView} /&gt

You need a better, scalable solution.

Redux helps you to handle such scenarios gracefully.

Great, but how can you use this with your React application?

Redux in itself is not bound to any UI framework, you can use it with Vue, React, Ember and a lot more. Having said that, it is only possible for a UI framework to use Redux through a UI binding library which ties Redux to your UI framework.

React Redux is the official Redux UI binding library for React

Before we see how we can implement react-redux for an application, we need to understand few terminologies

  1. The Store
  2. Action Creators
  3. Reducers

The Store

Store is a centralised place where all your application state sits. Consider it as your bank which holds all your cash. It looks something like this in Redux Dev Tools.

Action creators

Let’s take our previous analogy of Redux Store acting as a bank, now you would definitely want to either withdraw or deposit money right? And what would you do? You would raise a request to do so. Action creators acts as those requests. They emit actions which encapsulates the changes you want to make in the Store.

Ok moving on…

Reducers

Now once you have raised a request to deposit or withdraw something, you have to then submit that request to your bank manager (P.S-we are overlooking online banking here for a second :P). The bank manager knows exactly how to process that request, and if everything looks good, he/she will deposit or withdraw the amount requested, hand it over to you and update the details in your account to reflect the new changes.

This is what Reducers do for you in Redux. They take whatever action you pass, make a copy of the existing state, apply all your updates to the state & finally publish the updated state changes to the store.

React Redux Application Flow

  1. To begin with, we have our React Component which is encapsulated by connect() from react-redux which gives the component access to the store using mapStateToProps() as well as allows the component to dispatch actions using action creators.
  2. So, if we want to update something in the store, we will call action creators from our React component.
  3. These action creators will then dispatch specific actions.
  4. Our Reducers will interpret these actions and apply the requested changes to the store.
  5. Finally, we can access these updated changes of the store in our component through mapStateToProps().

Let’s play around with react-redux

Now as we know these building blocks a little better, it’s time to see how they come together in our application. I am going to keep this as simple as possible so you can understand the flow properly. We are going to create a simple list with 2 items either of which you can add to your favourite’s by clicking Favourite button and when you click again, the item will be removed from the favourite’s list.

Pre-requisite: You can setup a sample app using create-react-app. If at any point you feel stuck, you can refer to the source-code here:

Step 1. Install react-redux

npm install redux
npm install react-redux
npm install redux-devtools-extension

Step 2. Setting up store

Once you have installed all the above dependencies in your React app, the next step is to setup Redux Store in your application.

Add the following lines in your main index.js file from where you render your using ReactDOM.

Don’t worry if you are not clear what each of these lines do yet, I’ll explain you shortly 🙂


import { createStore } from "redux";
import { Provider } from "react-redux";
import { composeWithDevTools } from "redux-devtools-extension";

import rootReducer from "./reducers";

const store = createStore(  
rootReducer,  
{ itemActions: { favourites: [] } }, // sets initial state 
composeWithDevTools() // makes debugging through Redux Dev Tools possible
);
const rootElement = document.getElementById("root");
// Replace your existing ReactDOM.render() with below
ReactDOM.render(&ltProvider store={store}&gt&ltApp /&gt&lt/Provider&gt, rootElement);

Now let’s understand what exactly are we doing in the above code.

  • The createStore() from redux helps us to create an instance of our store.
  • We cannot directly pass this store to our &ltApp/&gt, the only way to pass store instance to our entire application is through &ltProvider/&gt component by react-redux. We pass a store attribute to this component whose value is our store instance.
  • Do you recall the store snapshot I showed earlier, that is possible only through Redux Dev Tools which enables developers in real time to view the State, Actions and a lot more fired across your application. It’s extremely useful for debugging, though a good idea would be to only enable it during development.
  • Next, we have imported something called rootReducer from reducers. rootReducers have all our reducers combined. We will be creating this file shortly.
  • Once we have imported everything, it’s time to instantiate our store. Our createStore() is taking rootReducers as first argument, our initial state object as second argument and lastly our composeWithDevTools() as third argument.
  • Finally, we are passing the store to component which encapsulates our component.

Step 3. Creating Action Creators

For our above example, we would be needing 2 action creators

  1. To add item in favourites.
  2. To remove item from favourites.

It’s a good practice to keep all your action creators in one directory. So, let’s create a directory named actions/ inside src/

Next, create an index.js file with actions/ directory.

Within that we will write our 2 action creators.


import { MARK_FAVOURITE, REMOVE_FAVOURITE } from "../constants/ActionTypes";

export const markFavourite = name => ({
  type: MARK_FAVOURITE, // mandatory key
  name
});

export const removeFavourite = name => ({
  type: REMOVE_FAVOURITE, // mandatory key
  name
});

  • Our action creators (markFavourite() & removeFavourite()) are just plain functions which returns an object describing what the action is.
  • The type key is mandatory as it states the action type in plain string. To avoid name collision the general practise is to define types as constants and place them in separate file like we did above.
  • The name key you see is just payload–any additional data which you need to update in store. You can add your own custom payload in whatever key you want.

Step 4. Creating Reducer

Creating action creator is just half the battle, we need something that understands those actions and updates our store. That is where Reducers come into picture.

Generally a real world application has multiple reducers, hence for clarity & better maintenance we should place all our reducers in a separate directory under src/ as reducers/

Next, let’s create our itemActions.js reducer file which will interpret our MARK_FAVOURITE & REMOVE_FAVOURITE actions.


const itemActions = (
  state = {
    favourites: []
  },
  action
) => {
  switch (action.type) {
    case "MARK_FAVOURITE":
      return {
        ...state,
        favourites: [...state.favourites, action.name]
      };
    case "REMOVE_FAVOURITE":
      const favourites = state.favourites.filter(item => item !== action.name);
      return {
        ...state,
        favourites
      };
    default:
      return state;
  }
};

export default itemActions;

    • itemActions is the name of our reducer as well as the key under which we would be able to access our favourites(Remember in the second step we passed initial state object which has itemActions key).

Note: The 1st argument of any reducer is the state which is initialised with default state for that reducer & the 2nd argument is the action which the reducer is going to interpret.- Within itemActions() exists a switch case which identifies what code should be executed for the give action type.

  • Now, in order to update state the most important thing to keep in mind is to not directly mutate the state. What I mean by that is, always create a copy of state object either using Object.assign() or spread operator like above and then update the object based on some condition.
  • For our MARK_FAVOURITE action, we are adding new item name to our favourites & for our REMOVE_FAVOURITE action we are filtering out the given item name.

Step 5. Combining Reducers

As real world application generally have more than one reducer, its necessary to pass all of them while we initialize the store. combineReducers() from redux precisely does that for you.

Let’s see how it’s done!!

First create an index.js file within your reducers/ directory


import { combineReducers } from "redux";
import itemActions from "./itemActions";

export default combineReducers({
  itemActions
});

Remember the rootReducers import which we did while creating store in Step 2, this is where it’s imported from.

So far we have covered the creation part of react-redux application, now it’s time to see how we can invoke our action creators from our component & access this state from store.

Step 5: Invoking Action Creators & Accessing State

In order for our component to have access to the Store & invoke action creators we would need some sort of binding function. connect() from react-redux does exactly that.
Let’s see how we can invoke this. Please refer to the complete code here:


...
import { connect } from "react-redux";
import { markFavourite, removeFavourite } from "../actions";

class ItemList extends React.Component {
// component code here

// action creators are now available in the props
updateFavourites = name => {
    return this.props.favourites.find(item => item === name)
      ? this.props.removeFavourite(name)
      : this.props.markFavourite(name);
  };

}
const mapStateToProps = state => ({
  favourites: state.itemActions.favourites
});

export default connect(
  mapStateToProps, 
  { markFavourite, removeFavourite }
)(ItemList);

  1. connect()

    1. mapStateToProps() which takes 2 arguments state(our redux store) & ownProps(props received from parent component)
    2. The 2nd argument is an object stating our action creators.

Note – Don’t make a mistake of directly importing and using action creators functions. You need to pass them to a connect() which makes a dispatch object available to our action creator using which an action is dispatched to the reducer which in turn updates the store.

  • Both the arguments are optional. If you wish to only access Store state in a particular component just pass mapStateToProps and keep 2nd argument null & vice versa.

If you were able to be with me so far, Kudos to you.

Conclusion

Though mastering Redux has a learning curve, taking the first step without getting overwhelmed is very crucial. I hope this post helps you create your first tangible react-redux application 🙂

Originally published at https://programmingwithmosh.com

#reactjs #react-js #redux #webdev #javascript

Getting started with React-Redux
9.95 GEEK