Recently, IN a project, I wanted to choose a tool for global status management of the project. After comprehensive consideration, I finally chose Redux. It is said that Redux is difficult to get started, today through 1 case, 3 functions to help partners quickly master and practice production!

As a front-end engineer, many friends may still be vague about the concept and use of Redux, and the mental burden of using it is also heavy.

However, Redux’s current ecosystem is very rich based on research, which makes it easier to introduce it as a status management tool for your project.

In this paper, the noun concept of Redux is reversed through a practical case, and the use of reduxJS/Toolkit module is simplified. I hope that through today’s sharing, we can help to break the knot, embrace Redux, improve work efficiency, and don’t work overtime from now on!

Thank you for your attention to xiaodong’s namesake public account: “DYBOY”, the first time to master the learning posture!

I. Redux foundation

Explaining the concept nouns at the beginning may make it more difficult for you to get started, so this section will only cover the basics of Redux.

1.1 What is Redux?

Redux is a JavaScript state container that provides predictable, debuggable, centralized state management.

1.2 the characteristics of

  • Predictable: Lets you develop applications with stable and predictable behavior that run in different environments (client, server, and native) and are easy to test.
  • Centralized management: Centrally managing your application’s state and logic allows you to develop powerful features such as undo/redo, state persistence, and more.
  • Debugable: Redux DevTools lets you easily track when, where, and how your app’s state changes. Redux’s architecture keeps track of every change, and with the help of “time travel debugging,” you can even send complete error reports to the server.
  • Flexible:Story withanyUI layer frame collocation use, it is small and lean (only2kB, including dependencies), and there areA huge plugin ecosystemTo fulfill your needs.

1.3 Design Idea

Since Redux is a state management library, it’s time to learn the basic data flow concepts and principles

(1) Single data source

The global state of the entire application is stored in an object tree that exists only in a single Store

A single data source makes it easy to develop homogeneous applications, and easier to maintain and manage state in a unified object tree!

(2) One-way data flow

  1. withstateTo describe the state of the application at a particular point in time
  2. Based on thestateTo render theView
  3. When something happens (for example, a user clicks a button),stateIt will update and generate new ones based on what’s happeningstate
  4. Based on the newstateTo renderView

(3) Immutability

The description of a state is usually a large JavaScript Object Tree, for example:

const state = {
    isLoading: true.userInfo: {
        uid: 1.wechat: 'DYBOY2020'.phone: 177* * * *7777.history: [1.2.3.4.5]}}Copy the code

Due to the dynamic nature of JS, objects can be modified, and Redux wants to record every state. If you modify the reference type properties in state directly, you will inevitably make state changes untraceable and unpredictable.

So state is read-only! The only way to change state is to trigger an action, which is a generic object that describes events that have occurred.

Redux expects all state updates to be immutable. Therefore, each state change will not modify the original object, but modify the clone of the previous state (state) to ensure immutability and correctness, while recording the state of each change.

(4) Pure functions update state

Pure function: a function that always gets the same output from the same input and has no side effects during execution.

In order to ensure the correctness of data changes and meet the requirement of state immutability, pure functions are introduced as the only way to update the state.

React Hooks state management incorporates Redux’s design ideas, after all, Redux’s author Dan Abramov is dead 🐶!

Ii. Case practice

Here’s how to plug into a brand new project, using the create-React-app scaffolding as an example.

With the help of @redux/ Toolkit, there is no longer any need to care deliberately about how to organize and compile Reducer, Action Creator, Action Type and other contents. Meanwhile, asynchronous Thunks are fused by default

Use useSelector(), useDispatch() to consume Store in any component, using Hooks in React 16.x.

2.1 Initializing the Project

The first is to initialize a TS + React environment with create-react-app

npx create-react-app craapp --template typescript
Copy the code

2.2 Installing Redux-related Dependencies

yarn add redux react-redux @reduxjs/toolkit
Copy the code
  • Redux: Core state management library
  • React -redux: The bridge layer for the React framework
  • Reduxjs/Toolkit: An assistant to ease the use of Redux

2.3 Creating a Global Store

All states are placed in Store, so you need a unified place to manage them. Take a counter as an example, and the file structure under./ SRC/Store looks like this:

. ├ ─ ─ index. Ts// Store instance, export the state and dispatch types└ ─ ─ reducers// Assemble all reducer files├ ─ ─ counter. TsReducer, action, selector for counters└ ─ ─ index. Ts// Export the rootReducers to consolidate all reducer files
Copy the code

(1) store/index.ts

import { configureStore } from "@reduxjs/toolkit";
import rootReducers from "./reducers"; Import the collection from the reducer

// instantiate store, globally unique
const store = configureStore({
  reducer: rootReducers,
});

// Export the state type in Store
export type RootState = ReturnType<typeof store.getState>;

// Export the type of Dispatch method that changed the state
export type AppDispatch = typeof store.dispatch;

// Export store by default for global Provieder consumption
export default store;
Copy the code

(2) store/reducers/index.ts

import {combineReducers} from '@reduxjs/toolkit'
import counterSlice from './counter' // We can introduce reducer

const rootReducers = combineReducers({
  counter: counterSlice // Define different reducer "namespace" in the form of MAP.
  / /... Here you can extend to add an arbitrary Reducer
})

// Export by default for configureStore consumption
export default rootReducers
Copy the code

(3) store/reducers/counter.ts

Now let’s look at how to create a Reducer easily. Previously, we needed to manually create multiple files (Reducer, Action, Action Creator) using Redux, but now we can directly use @redux/ Toolkit to put them in a unified file. Structurally describe actions and Redcuers in Redux.

import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { AppDispatch, RootState } from ".."; // The type declared in store/index.ts

Create reducer and Action with createSlice
const CounterSlice = createSlice({
  name: "counter".// Generate a prefix for the Action type, such as counter/increment
  initialState: {
    value: 0,},reducers: {
    increment: (state) = > {
      state.value += 1; // Immer is used by default and the original state will not be changed
    },
    decrement: (state) = > {
      state.value -= 1;
    },
    incrementByAmount: (state, action: PayloadAction<number>) = > {
      state.value += action.payload;
    },
    decrementByAmount: (state, action: PayloadAction<number>) = >{ state.value -= action.payload; ,}}});// Action Creator is used to execute the Action that returns a description of how to update state
export const { increment, decrement, incrementByAmount, decrementByAmount } =
  CounterSlice.actions;

// Asynchronous thunk, for cases where data needs to be processed asynchronously before it is updated
export const incrementAsync = (amount: number) = > (dispatch: AppDispatch) = > {
  setTimeout(() = > {
    dispatch(incrementByAmount(amount));
  }, 1500);
};

// Selector, as a function argument to useSelector to read data
export const counterSelector = (state: RootState) = > state.counter.value;

// Reducer, really execute the pure function that modifies state
export default CounterSlice.reducer;
Copy the code

The above can be used as a “template”, without worrying about combinations of concepts, just use it!

console.log(CounterSlice)
/* output: { name: 'counter', actions : { increment, decrement, incrementByAmount, decrementByAmount }, reducer } */
Copy the code

The above actions function is called Action Creator. For example, increment() returns:

{type: 'counter/increment'}
Copy the code

Executing incrementByAmount(5) returns:

{type: 'counter/incrementByAmount'.payload: 5}
Copy the code

2.4 Reading and writing Stores in Components

The first is the need to bind the Store instance to our app.

Add the following to./ SRC /index.tsx:

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux"; // Introduce Provider and bind store to application
import store from "./store"; // Introduce the store instance
import App from "./App";
import "./index.css";


ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>{/* Bind store */}<App />
    </Provider>
  </React.StrictMode>.document.getElementById("root"));Copy the code

React-redux provides useSelector() and useDispatch() to consume Counter state (data) in our custom Counter component.

/ / file location: / SRC/pages/counter index. The TSX
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { decrement, incrementAsync, counterSelector } from "@/store/reducers/counter";
import "./index.scss";

const CounterPage = () = > {
  const count = useSelector(counterSelector) // Read the count value
  const dispatch = useDispatch() // Get the dispatch and update the state with the action

  return (
    <div className="counter-page">
      <div className="counter">{/* sync - */}<div className="btn" onClick={()= > dispatch(decrement())}>
          -
        </div>
        <div className="val">{`${count}`}</div>{/* asynchronous + */}<div className="btn" onClick={()= > dispatch(incrementAsync(5))}>
          +
        </div>
      </div>
    </div>
  );
};

export default CounterPage;
Copy the code

Actual effect:

Looking at the whole case, versus not using@redux/toolkitSignificantly improve the efficiency of r & D, reduce the use of r & D mental burden!

Third, expand knowledge

3.1 @ redux/toolkit API

In the actual case above, the following API is used:

  • configureStore(): Simplifies Store creation, creates middleware to perform asynchronous execution by default, automatically enabledredux devtool
  • combineReducers(): Simplified mergereducerOperation and automatic injectionstateaction
  • createSlice(): Simplified and unified creationaction creator,reducer

The three apis above can be used for most scenarios, and this tool greatly reduces TypeScript type definitions.

Of course, to learn more about @redux/ Toolkit’s handy API, I recommend reading the official documentation:

  • @redux/ TooKit API manual
  • @redux/ Tookit API manual – TypeScript type specific

3.2 Redux status changes

If you are interested in Redux’s status update process and how it works, it is highly recommended to read:

  • How does Redux implement state changes to trigger page rendering?

3.3 Synchronous and asynchronous data flows for Redux

Synchronous data stream:

Asynchronous data flow:

Four,

Use Redux as a global state manager for the React project. Use Redux as a global state manager for the React project. Use Redux as a global state manager for the React project. Will it feel like React and Redux are brothers?

A quick summary:

  1. Redux is recommended for state management in React projects
  2. You need to understand design ideas in Redux
  3. It is recommended to use@redux-toolkit, can reduce the mental burden, significantly improve the efficiency of r&d
  4. When the master@redux-toolkitAfter that, you can read the original API of Redux and ask why@redux-toolkitTo do that?

Thank you for your attention to xiaodong’s namesake public account: “DYBOY”, the first time to master the learning posture!