[Redux] Redux Overview

When working with React components, it is critical to have a standardized way to handle the data flow. Flux was a popular state management library of React, but Redux replaces Flux.

Redux is a pattern and library for managing and updating application state, using events called actions.

https://redux.js.org/

Redux Libraries

Redux is a small standalone JS library, but it is commonly used with other libraries.

react-redux

  • Redux is most frequently used with React. “react-redux” is the official package that lets your React components interact with a Redux store.

Redux Toolkit

  • Redux Toolkit contains packages and functions that are essential for building a Redux app based on suggested best practices.

Why Redux?

  • React is about the structure, and Redux is data flow.
  • Redux is useful to handle complex data flows (inter-component (non-hierarchical) communications)
  • The same data can be used in multiple places.

Note that it is not required to use Redux to create a React app. In many cases, you can create a perfectly working React app without Redux. 

Redux – 3 Principles

  • A single immutable Store
  • User actions are done through Actions
  • The “State” is changed by pure functions (Reducers)

Actions

  • An action is a way to initiate the change of the existing state.
  • An action is a plain JavaScript object of “type” – string – and “payload” -object -.
    • type: (string) a descriptive name of an action
    • payload: (object) additional information.
  • An “action creator” function returns an action object.

When an action is dispatched by a “Store,” reducers will handle the data and update the “Store.”

const doSomething = (payload) => {
  return {
    type: 'ACTION_NAME',
    payload: payload
  }
};

Store

In Redux, a “Store” and the “Change logic” are separate. There is only one “Store,” which is immutable.

  • The store is created by passing in a reducer.
import { createStore } from 'redux';

// createStore(reducer, preloadedState, enhancer)
const store = createStore(reducer);

Note that there is NO “setState()” in the “Store.” The only way to update the “Store” is through dispatching the actions (and reducers).

  • store.dispatch(action)
  • store.subscribe(listener)
  • store.getState()

dispatch

The only way to update a state object in the Store is to call store.dispatch() with an action object. The store will run all registered reducer functions.


Immutability

Rather than modifying the current state, you need to return a new state object. (A state object cannot be modified.)

You can use the following techniques:

  • Object.assign(target, …source)
  • Spread operator
let newState 
  = Object.assign({}, oldState, {payload: newValue});

let newState = { ...oldState, payload: newValue };

The biggest benefit of immutable objects in Redux is the logic of how to check the state has been modified. By replacing the state object rather than modifying the existing one, Redux can just compare the old state and the new state using the equality operator. It is a significant performance boost.

if (previousStoreState !== storeState) {
}

Also, you can just save the state history just by saving all state objects.


Reducers

A Reducer is a pure function that returns a new state. 

A Reducer

  • accepts the current state and action as arguments
  • creates a newly updated state
    • DO NOT modify the existing state object
  • returns the new state object -> The Store will be updated.

All Reducers are called on each action dispatch.

The switch statement with a default (returns the old state) is the common pattern of a reducer.

As a pure function, a Reducer 

  • may not mutate arguments
  • may not perform any side-effect
  • may not call any non-pure functions
const myReducer = (state={}, action) => {
  switch(action.type) {
    case 'ACTION_NAME':
      return Object.assign({}, state, {payload: action.payload});
    default:
      return state;
  }
};

Root Reducer

Rather than creating one giant reducer with dozens of case statements, you might want to create many small reducers.

It is a common pattern that you combine all reducers in a single reducer (Root reducer) and provides the root reducer to the Store.

import { combineReducers, createStore } from 'redux';

const rootReducers = combineReducers({
  categoryList: categoryReducers.categoryListReducer,
  categoryCreate: categoryReducers.categoryCreateReducer,
  directorList: directorReducers.directorListReducer
});

const store = createStore(rootReducers);

Redux Cycle (Flow)

  • One way (unidirectional) flow
  • Single “Store” with hierarchical (nested) reducers

Action Creator (function) -> Action (object) -> dispatch -> Reducer -> Store -> React components

  • Initial Setup
    1. combineReducers(), createStore(): A Store is created using a root reducer function.
    2. The Store call the root reducer once, and saves the return value as an initial state.
    3. UI is rendered using the initial state of the Store.
  • Updates
    1. A user interacts with the UI
    2. store.dispatch(): the application dispatches an action to the Store.
    3. The store runs the reducer function with the previous state and the new action object (type and payload).
    4. The Store saves the new state.
    5. The Store notifies all parts of the UI that are subscribed.
    6. Each UI component update itself using the new state.

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s