Home > Tutorials > Web Design > Redux

Simple Redux store example with React + Hooks

Last updated : 29 January 2022

Why do we need Redux?

React supports only one-way data flow down the component hierarchy. That means a parent component can pass data to its child component. But the child component cannot pass data to its parent component. This one-way data flow is also called one-way binding. One-way binding keeps everything modular and fast.

However, there are several problems associated with React's one-way data binding.

  • When a child component needs data from a higher-order parent component, that data has to pass through all the components hierarchy regardless the intermediate components need that data or not. That is called prop drilling.
  • If a component needs data from another component in a different/independent component tree, there is no way to achieve that.

The below picture explains the above scenarios.

Figure 1 : Reactjs Prop drilling

What is Redux?

In simple words, Redux is centralized storage for shared data. Redux allows us to store the application state independently from components and make it accessible to any component. The Redux store is accessible from any component regardless of the component hierarchy/tree.

Main concepts of Redux

There are three main concepts of Redux.

  • Action creators
  • Reducers
  • Store

The store holds the state shared amongst components. Action creators define events that dispatch actions to interact with the store. Reducers receive the actions dispatched by action creators. Reducers also perform the relevant updates to the store and return the updated state. Now let's discuss these three in detail.

Action creators

Actions are plain Javascript objects. Action Objects are events that send information to the Redux store. Actions contain properties such as type to identify the type of action and payload to take action on. Actions are created by action creators and dispatched via the dispatch method. Action creators are just Javascript functions.

export interface action { type: string, payLoad: number }

export const incrementCount = (inc: number) => {
    return { type: "increment", payLoad: inc };
}

export const decrementCount = (dec: number) => {
    return { type: "decrement", payLoad: dec };
}

In the above example, the Javascript methods incrementCount() and decrementCount() return action objects with action types and payloads.

Redux dispatch method

Actions are dispatched with Redux's dispatch method. The React hook useDispatch() provides the dispatch method to dispatch actions to the Reducers. The dispatch method (or any hook call) is only allowed in a React rendering component.

Reducers

Reducers are pure Javascript functions. Reducers perform actions on the current state of the application and return the updated application state. In other words, reducers listen to actions dispatched by action creators and update the application state based on the action type.

import { action } from './actions'

export interface shoppingCartState { count: number }
let initialState: shoppingCartState = { count: 0 };

export const shoppingCartReducer = (state = initialState, action: action) => {
    switch (action.type) {
        case 'increment':
            return { ...state, count: state.count + action.payLoad };
        case 'decrement':
            return { ...state, count: state.count - action.payLoad };
        default:
            return state;
    }
}

The Reducer captures every action type returned by action creators and returns the appropriate mutated state.

Redux Store

The store holds the application state. The store is created with the createStore() method that takes Reducers as a parameter. If you have multiple Reducers for specific parts of the state, those Reducers can be combined as below.

import { createStore } from 'redux'
import { shoppingCartReducer } from './reducers'

export const store = createStore(shoppingCartReducer);

The below image shows roughly how Redux manages the state.

Figure 2 : Redux state management

Assembling the React-Redux store app

Now let's wire our Redux components to create a meaningful app, a simple shopping cart. The shopping cart consists of several independent and unrelated components. All the components have access to the store.

Our app has two components. A header with a counter and a container with some functionality to increment and decrement that counter.

import { useSelector } from "react-redux"
import { shoppingCartState } from '../../store/reducers'
export const Header = () => {
    const count = useSelector((state: shoppingCartState) => state.count)
    return (
        <p>Shopping cart : {count}</p>
    )
}

The above component uses the useSelector() hook to bind the count prop that belongs to the Redux store. The count is mutated by the Content component.

import { useDispatch } from "react-redux"
import { incrementCount, decrementCount } from '../../store/actions'
export const Content = () => {
    const dispatch = useDispatch();

    return (
        <>
            <p><button onClick={() => dispatch(incrementCount(1))}>Add 1 to cart</button></p>
            <p><button onClick={() => dispatch(decrementCount(1))}>Delete 1 from cart</button></p>
        </>
    )
}

The Content component updates the count prop by dispatching actions.

Lance
By: Lance
Lance is a software engineer with over 15 years of experience in full-stack software development.
Read more...

Leave a comment

No Comments