UI Development

Getting Started with Redux

What is Redux?

Created by Dan Abramov and Andrew Clark in 2015, Redux is an open-source JavaScript library for managing application state.

Although Redux can be used with any view library, it is commonly used with React.

Why to use Redux?

Every useful thing solves some problem, Redux also solves a problem and its not state management as React already does it.

Redux helps managing State but the problem it solves is Data Flow.

What’s the problem with React Data Flow?

React has one-way data flow as data is passed down the component tree via props and to pull the data up in the tree needs a callback function, means callback function must be passed down to any component that wants to pass data up.

Sooner or later a situation arrives when there is some data in top level component and a child 5 levels down needs that data, now that data needs to be woven through the intermediate components that don’t need that data.
So as application size increases it becomes unmanageable to handle the data, here comes Redux to solve the problem.

How Redux manages data?

It gives direct access of data needed by component, any component can plug into Redux data store and pull the data it requires.

Wait, Redux can also do some other cool stuff!

Yes, along with solving the data flow problem it can also do some cool stuff like:-

  • Easy debugging of data and its flow by using Redux DevTools as it allows inspecting every state change.
  • Time Travel debugging, as it is easy to roll back state changes and see how application looked in past.

  • Makes code more maintainable.

Understanding Redux

Redux is similar to (and inspired by) Facebook’s Flux architecture and revolves around 3 core concepts:

  1. There is a single source of truth

    Application data is represented by a single JavaScript object, known as State of the application.

    {
        user: {}
        products: {}
        cart: [{
                id: 1,
                quantity: 2
        }],
        favorites: [],
        ...
    }
  2. State(data) is read-only

    State can not be manipulated directly, its immutable, it can only be changed by emitting an action.
    Action is an object having information about type of event and it may also contain data that needs to be updated.

    {
        type: 'ADD_TO_CART'
        payload: {
            id: 1,
            quantity: 2
        }
    }
  3. Changes are made with pure functions

    There is a single function that listens for the actions, known as Reducer.
    Reducer is basically a big switch statement that has case for every action type and returns a new state without impacting the original state.
    This function must be pure function, a pure function returns the same value every time for a set of arguments passed to it.

    function cartReducer(state = initialState, action) {
        switch(action.type) {
            case 'ADD_TOCART': {
                return {...state, cart: [...state.cart, action.payload]}
            }
            default: {
                return state;
            }
        }
    }

     

How Redux Works?

Installation

// via npm
npm install redux
 
// via yarn
yarn add redux

Creating Redux Store

createStore(reducer) creates a Redux store, which keeps the state tree of the application and reducer is a mandatory parameter to be passed while invoking createStore method.
// importing the createStore method from redux
import { createStore } from "redux";
 
// creating the store by invoking createStore method by providing reducer as a parameter
const store = createStore(reducer);

createStore method return an Object with state of the application along with the methods like:-

  • dispatch – for dispatching the actions to the reducers, this is the only way to mutate the state

  • getState() – for getting the current state of the application.
  • subscribe() – to update the UI automatically when state get changes.
// importing the createStore method from redux
import { createStore } from "redux";
 
// initialState
const initialState = {
    user: []
    products: [],
    cart: [],
    fav: []
}
 
// reducer
function cartReducer(state = initialState, action) {
    switch(action.type) {
        case 'ADD_TOCART': {
            return {...state, cart: [...state.cart, action.payload]}
        }
        default: {
            return state;
        }
    }
}
 
// creating the store by invoking createStore method by passing the reducer
const store = createStore(cartReducer);
 
// subscribing to state changes, usually we use "React Redux" view binding library instead of directly subscribing.
store.subscribe(() => console.log(store.getState()));
 
// dispatching action
store.dispatch({
    type: 'ADD_TO_CART'
    payload: {
        id: 1,
        quantity: 2
    }
});
 
// dispatching the above action will change the state of application and console will log the current state.
/*
{
    user: []
    products: [],
    cart: [{
        id: 1,
        quantity: 2
    }],
    fav: []
}
*/

 

When working with React applications, to subscribe for state changes we use “React Redux” view binding library instead of using subscribe method directly.

What is React Redux library?

React Redux is official binding between React view library and Redux state management library. It allows React components to read data from Redux store and also dispatch actions to mutate the application state.

Installation

// via npm
npm install react-redux
 
// via yarn
yarn add react-redux

Using React Redux library

Library provides some useful syntactic sugar bindings for React components like:-

  • Provider – this component makes Redux store available to the React application, it is implemented as a wrapper component of the React application.

    It needs store as a prop, store is the single Redux store of the application created using Redux createStore method.

    import React from 'react'
    import ReactDOM from 'react-dom'
     
    // importing Provider form React Redux binding library
    import { Provider } from 'react-redux'
     
    // importing the store, its same as we created store in previous example but in a different file
    import store from './store'
     
    // importing the React application
    import App from './App'
     
    const rootElement = document.getElementById('root')
    ReactDOM.render(
    // Provider must be top level component off the application along with the store as a prop
      <Provider store={store}>
        <App />
      </Provider>,
      rootElement
    )

     

  • connect() – it is used to bind the Redux store to specific React components. These type of React components are known as connected components.

    connect method has some parameters and two of the parameters which are used to get state data and actions are:- mapStateToProps and mapDispatchToProps

    // importing connect method from React Redux binding library
    import { connect } from 'react-redux'
     
    // importing a component, for eg. Cart component which needs to be connected to cart data in the Redux store
    import Cart from 'Components/Cart';
     
    // creating 'action creators', these are the functions which return the action objects
    const addToCartAction = (id, quantity) {
        return {
            type: 'ADD_TO_CART'
            payload: {
                id: 1,
                quantity: 2
            }
        }
    }
     
    // mapping state data to component as a props, it is provided with the state of the Redux store.
    // Syntax is: mapStateToProps?: (state, ownProps?) => Object.
    const mapStateToProps = (state) => {
      return {
        // now 'cart' prop will be provided to the 'Cart component' holding cart data as a prop
        cart: state.cart
      }
    }
     
    // mapping actionCreators to component as a props, it is provided with the dispatch method of the Redux store.
    // Syntax is: mapDispatchToProps?: Object | (dispatch, ownProps?) => Object
    const mapDispatchToProps = (dispatch) => {
        // now 'addtoCart' method will be provided to the 'Cart component' as a prop which when invoked will dispatch the addtoCartAction
        addToCart: () => dispatch(addToCartAction)
    }
     
    // invoking connect method with the mapStateToProps and mapDispatchToProps methods as a parameter.
    export default connect(
      mapStateToProps,
      mapDispatchToProps
    )(Cart)

Want more details of the libraries?

In this post I tried to give a brief and easy to understand intro to the Redux library along with the React Redux library. There is much more these libraries provide and if you want to dig deeper, below are the links to be referred:-

About The Author