🖍️
Developer Note
  • Welcome
  • Git
    • Eslint & Prettier & Stylelint & Husky
  • Programming Language
    • JavaScript
      • Script Async vs Defer
      • Module
      • Const VS Let VS Var
      • Promise
      • Event Loop
      • Execution Context
      • Hoisting
      • Closure
      • Event Buddling and Capturing
      • Garbage Collection
      • This
      • Routing
      • Debounce and Throttle
      • Web Component
      • Iterator
      • Syntax
      • String
      • Array
      • Object
      • Proxy & Reflect
      • ProtoType
      • Class
      • Immutability
      • Typeof & Instanceof
      • Npm (Node package manager)
    • TypeScript
      • Utility Type
      • Type vs Interface
      • Any vs Unknown vs Never
      • Void and undefined
      • Strict Mode
      • Namespace
      • Enum
      • Module
      • Generic
    • Python
      • Local Development
      • Uv
      • Asyncio & Event loop
      • Context Manager
      • Iterator & Generator
      • Fast API
      • Pydantic & Data Class
    • Java
      • Compilation and Execution
      • Data Type
      • Enumeration
      • Data Structure
      • Try Catch
      • InputStream and OutputStream
      • Concurrent
      • Unicode Block
      • Build Tools
      • Servlet
      • Java 8
  • Coding Pattern
    • MVC vs MVVM
    • OOP vs Functional
    • Error Handling
    • MVC vs Flux
    • Imperative vs Declarative
    • Design Pattern
  • Web Communication
    • REST API
      • Web Hook
      • CORS issue
    • HTTPS
    • GraphQL
      • REST API vs GraphQL
      • Implementation (NodeJS + React)
    • Server-Sent Event
    • Web Socket
    • IP
    • Domain Name System (DNS)
  • Frontend
    • Progressive Web App (PWA)
    • Single Page & Multiple Page Application
    • Search Engine Optimiaztion (SEO)
    • Web bundling & Micro-frontend
      • Webpack
        • Using Webpack to build React Application
        • Using Webpack to build react library
      • Vite
      • Using rollup to build react library
      • Implementing micro frontend
    • Web Security
      • CSRF & Nonce
      • XSS
      • Click hijacking
    • Cypress
    • CSS
      • Core
        • Box Model
        • Inline vs Block
        • Flexbox & Grid
        • Pseudo Class
        • Position
      • Tailwind CSS
        • Shadcn
      • CSS In JS
        • Material UI
    • React
      • Core
        • Component Pattern
        • React Lazy & Suspense
        • React Portal
        • Error Boundary
        • Rendering Methods
        • Environment Variable
        • Conditional CSS
        • Memo
        • Forward Reference
        • High Order Component (HOC) & Custom Hook
        • TypeScript
      • State Management
        • Redux
        • Recoil
        • Zustand
      • Routing
        • React Router Dom
      • Data Fetching
        • Axios & Hook
        • React Query
        • Orval
      • Table
        • React Table
      • Form & Validation
        • React Hook Form
        • Zod
      • NextJS
        • Page Router
        • App Router
      • React Native
    • Angular
    • Svelte
      • Svelte Kit
  • Backend
    • Cache
      • Browser Cache
      • Web Browser Storage
      • Proxy
      • Redis
    • Rate limit
    • Monitoring
      • Logging
      • Distributed Tracing
    • Load Test
    • Encryption
    • Authentication
      • Password Protection
      • Cookie & Session
      • JSON Web Token
      • SSO
        • OAuth 2.0
        • OpenID Connect (OIDC)
        • SAML
    • Payment
      • Pre-built
      • Custom
    • File Handling
      • Upload & Download (Front-end)
      • Stream & Buffer
    • Microservice
      • API Gateway
      • Service Discovery
      • Load Balancer
      • Circuit Breaker
      • Message Broker
      • BulkHead & Zipkin
    • Elastic Search
    • Database
      • SQL
        • Group By vs Distinct
        • Index
        • N + 1 problem
        • Normalization
        • Foreign Key
        • Relationship
        • Union & Join
        • User Defined Type
      • NOSQL (MongoDB)
      • Transaction
      • Sharding
      • Lock (Concurrency Control)
    • NodeJS
      • NodeJS vs Java Spring
      • ExpressJS
      • NestJS
        • Swagger
        • Class Validator & Validation Pipe
        • Passport (Authentication)
      • Path Module
      • Database Connection
        • Integrating with MYSQL
        • Sequalize
        • Integrating with MongoDB
        • Prisma
        • MikroORM
        • Mongoose
      • Streaming
      • Worker Thread
      • Passport JS
      • JSON Web Token
      • Socket IO
      • Bull MQ
      • Pino (Logging)
      • Yeoman
    • Spring
      • Spring MVC
      • Spring REST
      • Spring Actuator
      • Aspect Oriented Programming (AOP)
      • Controller Advice
      • Filter
      • Interceptor
      • Concurrent
      • Spring Security
      • Spring Boot
      • Spring Cloud
        • Resilience 4j
      • Quartz vs Spring Batch
      • JPA and Hibernate
      • HATEOS
      • Swagger
      • Unit Test (Java Spring)
      • Unit Test (Spring boot)
  • DevOp
    • Docker
    • Kubernetes
      • Helm
    • Nginx
    • File System
    • Cloud
      • AWS
        • EC2 (Virtual Machine)
        • Network
        • IAM
          • Role-Service Binding
        • Database
        • Route 53
        • S3
        • Message Queue
        • Application Service
        • Serverless Framework
        • Data Analysis
        • Machine Learning
        • Monitoring
        • Security
      • Azure
        • Identity
        • Compute Resource
        • Networking
        • Storage
        • Monitoring
      • Google Cloud
        • IAM
          • Workload Identity Federation
        • Compute Engine
        • VPC Network
        • Storage
        • Kubernetes Engine
        • App Engine
        • Cloud function
        • Cloud Run
        • Infra as Code
        • Pub/Sub
    • Deployment Strategy
    • Jenkins
    • Examples
      • Deploy NextJS on GCP
      • Deploy Spring on Azure
      • Deploy React on Azure
  • Domain Knowledge
    • Web 3
      • Blockchain
      • Cryptocurrency
    • AI
      • Prompt
      • Chain & Agent
      • LangChain
      • Chunking
      • Search
      • Side Products
Powered by GitBook
On this page
  • Definition
  • Reducer
  • Store
  • Action
  • Redux Saga
  • Redux Toolkit

Was this helpful?

  1. Frontend
  2. React
  3. State Management

Redux

PreviousState ManagementNextRecoil

Last updated 11 months ago

Was this helpful?

Definition

  • Redux is a library for managing and updating application state library which is one-way data flow

  • Redux only have one store which can be an object and be used globally

  • Components will subscribe to the store and the store will notify to its component when changes

  • Reducer is used to take the input and update the store

  • When component want to make change to the store, it needs to dispatch the action which is described the the changes, and reducer take the action as a input and change the store

Reducer

  1. Contain different business logics and return different states based on the actions.

  2. Should be a pure function (not contain other data sources, such as api)

  • Example:

import { combineReducers } from "redux";
interface product {
  name: string;
  price: number;
  qty: number;
  type: number;
}

interface receipt {
  subtotal: number;
  total: number;
  tax: number;
}

const defaultReceipt: receipt = {
  subtotal: 0,
  total: 0,
  tax: 0,
};

interface action {
  type: string;
  content: receipt;
}

const receipt = (state = defaultReceipt, actions: action) => {
  switch (actions.type) {
    case "SET_RECEIPT": {
      return actions.content;
    }
    default:
      return state;
  }
};

const productList = (state = [], actions: action) => {
  switch (actions.type) {
    case "SET_LIST": {
      return actions.content;
    }
    default:
      return state;
  }
};

const ReceiptApp = combineReducers({
  receipt,
  productList,
});

export default ReceiptApp;

Store

  1. The whole global state of your app is stored in an object tree inside a single store.

  2. Dispatch method is to send the action to reducer to change the state tree

  • Example:

import { Provider } from "react-redux";
import ReceiptApp from "./reducers";
import { createStore } from "redux";

let store: any = createStore(ReceiptApp, {});

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);
  • Example (dispatch):

import { useDispatch } from "react-redux";
import { setReceipt, setProductList } from "../actions";

const Inputform: React.FC<{
  createReceipt: (input: receipt) => void;
  createProductList: (input: product[]) => void;
}> = ({ createReceipt, createProductList }) => {
  const dispatch = useDispatch();
  const createReceipt = (input: receipt) => {
      dispatch(setReceipt(input));
  }
  const createProductList = (input: product[]) => {
      dispatch(setProductList(input));
  }
  ...
}

export default Inputform;
  • Example (get the state):

import { useSelector } from "react-redux";

const Receipt: React.FC<{
}> = () => {
  const receiptData:receipt = useSelector((state) => state.receipt);
  const productListData:product[] = useSelector((state) => state.productList);
  ...
}

export default Receipt;

Action

  1. Type is to describe the name of event happened

  2. Content/Payload is to describe the change after event

  3. After dispatching the action, it will pass the argument to the reducer to perform business logic and changed the state tree

  • Example:

export const setReceipt = (input: receipt) => {
  //console.log(input);
  return {
    type: "SET_RECEIPT",
    content: input,
  };
};

export const setProductList = (input: product[]) => {
  //console.log(input);
  return {
    type: "SET_LIST",
    content: input,
  };
};

Redux Saga

Why Saga?

If we involve the the business logic related to async function (such api call), we need saga to implement it instead of doing it in the reducer, since reducer should be a pure function, also the promise function will be returned instead of the resolve value if the reducer becomes a async function

Flow

Pre-Condition

npm install redux-saga

Link With reducer

  • We create rootSaga , apply the saga middleware with reducer to the store and run the rootSaga

// index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";
import Count from "./redux/reducer";
import { applyMiddleware, createStore } from "redux";
import createSagaMiddleware from "redux-saga";
import rootSaga from "./saga";

const sagaMiddleWare = createSagaMiddleware();
const store = createStore(Count, applyMiddleware(sagaMiddleWare));
sagaMiddleWare.run(rootSaga);
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

RootSaga

  • Divided into 2 parts - worker and watcher

  • Worker: Containing logic to implementing the async function ( use call method) and trigger the existing action ( use put method)

  • Watcher: Monitoring the specific action, if the type of specific action is called, the function of the worker will be triggered, monitoring function mainly contain 2 types : takeLatest and takeEvery. takeLatest: If the previous worker still not finished, the new worker will start and the previous one will be cancelled. takeEvery: even the previous worker is not finished, the new worker can be triggered, and the previous worker is still running at the same time

import {
  delay,
  put,
  takeEvery,
  all,
  takeLatest,
  call,
} from "redux-saga/effects";
import {
  addCount,
  minCount,
  addAsync,
  getApiData,
  getApiDataAsync,
} from "../redux/action";
import { callAPI } from "../api";

// Saga Worker
function* addAsyncSaga() {
  yield delay(1000);
  yield put(addCount());
}

function* getApiDataSaga() {
  const data = yield call(callAPI, "https://sampleapis.com/futurama/api/info");
  yield put(getApiData(data));
}

// Saga Watcher
export default function* rootSaga() {
  yield all([
    takeLatest(addAsync().type, addAsyncSaga),
    takeLatest(getApiDataAsync().type, getApiDataSaga),
  ]);
}

Redux Toolkit

Why need redux toolkit

  • The action is structured, contains payload and type,

  • createSlice automatically generates a slice reducer with corresponding action creators and action types. so longer need to map the reducer and action by type

  • As the state is immutable, if you need to change the value, you must need to return new object and do the logic on it

function plainJsReducer(state, action) {
  // Add 3 points to Ravenclaw,
  // when the name is stored in a variable
  const key = "ravenclaw";
  return {
    ...state, // copy state
    houses: {
      ...state.houses, // copy houses
      [key]: {  // update one specific house (using Computed Property syntax)
        ...state.houses[key],  // copy that specific house's properties
        points: state.houses[key].points + 3   // update its `points` property
      }
    }
  }
}
  • By using redux-toolkit, since its reducer function already include immer, so you can change the value with user-friendly syntax without mutating the state

function immerifiedReducer(state, action) {
  const key = "ravenclaw";

  // produce takes the existing state, and a function
  // It'll call the function with a "draft" version of the state
  return produce(state, draft => {
    // Modify the draft however you want
    draft.houses[key].points += 3;

    // The modified draft will be
    // returned automatically.
    // No need to return anything.
  });
}

Implementation

  • Main (Group the slices together)

import { combineReducers } from "@reduxjs/toolkit";
import roomReducer from "./slice/roomSlice";
import userReducer from "./slice/userSlice";

const rootReducer = combineReducers({
  roomInfo: roomReducer,
  userInfo: userReducer,
});

export type RootState = ReturnType<typeof rootReducer>;
export default rootReducer;
  • Slice (Define reducer and action and their relationship)

import { createSlice, PayloadAction } from "@reduxjs/toolkit";

type roomState = {
  name: string;
};

const roomInfoSlice = createSlice({
  name: "roomInfo",
  initialState: { name: "New Room" },
  reducers: {
    setCurrentRoomName(room, action: PayloadAction<string>) {
      //console.log(action, room);
      const { payload } = action;
      room.name = payload;
      //return room;
    },
  },
});

export const { setCurrentRoomName } = roomInfoSlice.actions;

export default roomInfoSlice.reducer;
  • Store

import React from "react";
import ReactDOM from "react-dom";
import * as serviceWorker from "./serviceWorker";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Dashboard from "./pages/dashboard";
import Index from "./pages";
import { Provider } from "react-redux";
import rootReducer from "./reducer";
import { configureStore } from "@reduxjs/toolkit";

const store = configureStore({ reducer: rootReducer });

ReactDOM.render(
  <Provider store={store}>
    <Router>
      <div>
        <Switch>
          <Route path="/dashboard" component={Dashboard} />
          <Route path="/" component={Index} />
        </Switch>
      </div>
    </Router>
  </Provider>,
  document.getElementById("root")
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
  • Dispatch

import React from "react";
import { useDispatch } from "react-redux";
import { setCurrentRoomName } from "../../reducer/slice/roomSlice";
const ChatRoomCard: React.FC<{
  name: string;
  switchToRoom: () => void;
}> = ({ name = "", switchToRoom }) => {

  const dispatch = useDispatch();
  
  const enterRoom = () => {
    dispatch(setCurrentRoomName(name));
    switchToRoom();
  };

  return (
    ...
  )
};

export default ChatRoomCard;
  • State

import React from "react";
import { useSelector } from "react-redux";
import { RootState } from "../../reducer";
const Header: React.FC<{
  switchToMenu: () => void;
}> = ({ switchToMenu }) => {
  const { name } = useSelector((state: RootState) => state.roomInfo);
  return (
     ...
  );
};

export default Header;