One, foreword

In general React projects, Redux or React-Redux will be used as our state management tool in most cases. When using Vuex, we can write synchronous operation in mutations or asynchronous operation in actions. However, Redux is different from Vuex. Redux itself does not support asynchrony, so if we need to handle asynchronous operations, we need to install additional middleware such as Redux-Thunk or Redux-Saga, which is cumbersome.

If you use React Hook, Redux, Redux-Thunk, or Redux-Saga in your React projects, you may want to use the Redux-Toolkit (RTK) to optimize your project structure and make your code look cleaner.

TSX # Optimize/Counter Slice.ts index.tsx # optimize/Counter Slice.ts index.tsxCopy the code

Second, the introduction of

RTK is designed to help solve several problems with Redux:

  • Configuration complex, devtool…
  • Constant, action, reducer…
  • Many dependencies need to be added, such as redux-Thunk, redux-Saga, immer…

To put it simply, the Redux configuration process is too complicated. A series of functions such as actionTypes, Actions, Reducer, and store need to be written to complete the Redux configuration. Finally, the functions need to be inserted into props through connect for use by components. With RTK, only a reducer is required.

So what exactly is RTK?

Third, core dependence

We opened GitHub and found the source code for RTK. You can see some of the dependencies used by RTK in the Toolkit /package.json directory:

What do these dependencies mean?

The first:redux

Of course, RTK needs it to work, and the introduction of Redux means that if our project has RTK installed, we don’t need to re-install Redux; In addition, if you look carefully enough, you’ll notice that RTK’s dependencies do not include React-Redux. This means that RTK is a standalone library. It’s not just for react projects, but you can use it in any environment if you want, like Vue or Angular, or even jQuery. Or native JS.

The second:redux-thunk

RTK comes with Redux-Thunk to handle asynchronous logic, which is turned on by default in RTK (you can turn it off manually during development, or install other asynchronous middleware like Redux-Saga if you wish).

The third:reselect

ShouldComponentDidUpdate is similar to shouldComponentDidUpdate, but they are not a thing. Here is a primer for RTK. This plug-in is not covered in depth.

Fourth:immer

The last immer is an interesting plugin that allows us to turn state immutable into mutable, that is, we can directly modify data in the Reducer function (when I first heard this idea, my head told me that this was not true, This violates the concept of reducer function programming. Redux is a state management tool for unidirectional data flow, and data is immutable. We cannot directly modify the state of store, but complete state update by returning new state and replacing old state. Or relatively resistant). To sum up, you can choose to modify state by using immer to make data mutable, or you can choose not to use immer and keep data immutable. It’s up to you. (Isn’t that what makes React so flexible?)

About immer library

In the previous article, we gave a brief introduction to IMmer, which is discussed separately here

What exactly is immer? As mentioned above, immer is an interesting plugin that allows us to convert state immutable to mutable. In fact, the underlying core implementation of IMmer uses ES6’s proxy object to intercept state changes. In addition, the proxy automatically returns immutable objects by replacing them sequentially.

Here is an example of the difference between using immer and not using imMER:

// Do not use immer, return the new state, replace the old state
reducers: {
  fetchStart(state) {
    return { ...state, loading: true };
  },
  fetchEnd(state) {
    return { ...state, loading: false };
  },
  fetchSuccess(state, action: PayloadAction<FetchType>) {
    return { ...state, data: action.data };
  },
  fetchFailure(state, action: PayloadAction<ErrorType>) {
    return { ...state, error: action.message }; }},// Use immer to modify the original state without returning to the new state
reducers: {
  fetchStart(state) {
    state.loading = true;
  },
  fetchEnd(state) {
    state.loading = false;
  },
  fetchSuccess(state, action: PayloadAction<FetchType>) {
    state.data = action.data;
  },
  fetchFailure(state, action: PayloadAction<ErrorType>) {
    state.error = action.message
  },
},
Copy the code

In the above example, the number of lines of code using immer is the same as the number of lines of code not using imMER, and there is no simplification, so what are the advantages of immer?

Look at the following example:

// Do not use immer, return the new state, replace the old state
reducers: {
  someReducer(state, action: PayloadAction<SourceData>) {
    return {
      ...state,
      first: {
        ...state.first,
        second: {
          ...state.first.second,
          third: {
            ...state.first.second.third,
            value: action.someValue, }, }, }, }; }},// Use immer to modify the original state without returning to the new state
reducers: {
  someReducer(state, action: PayloadAction<SourceData>){ state.first.second.third.value = action.someValue; }},Copy the code

This is a good example. If our state is nested in many layers, and the data that needs to be modified is at a very deep level, then the convenience of IMmer comes into play, with two main benefits:

  • Writing greatly simplified, a logic line of code can be realized
  • When objects are deeply nested, write them manuallyimmutable updateThe logic of reducer is very difficult, and users may make careless mistakes when modifying the state in reducer and enable itimmerThere’s a good way to get around this

Core API

  • configureStore

CreateStore is a wrapper around the createStore function in standard Redux. It adds some configuration to store for a better development experience, wraps createStore, and integrates with Redux-thunk, Redux DevTools, and is enabled by default

  • getDefaultMiddleware

Returns an array containing the default Middleware set. By default, configureStore automatically adds middleware to the Store Settings. If you want to customize the Middleware list, you can add your own middleware to the array returned by getDefaultMiddleware.

  • createReducer

Simplifies the Reducer function in standard Redux. Immer is built-in (enabled by default), which greatly simplifies the update logic of IMMER by writing mutable code in reducer (the IMMER library is described in detail above). In addition, when creating Reducer using the createReducer function in RTK, There are two ways to create it, one way to call back functions and one way to map objects. (I prefer the latter because the way the mapped objects are written seems more intuitive and easier to understand.)

  • createAction

Used to create and define functions of the standard Redux type. Pass in a constant type that returns a function carrying payload, much like the action in standard Redux.

  • createSlice

This createSlice function, which I think is the core API in RTK, is described in the official documentation as follows: This function receives an initialization state object and a Reducer object, which can slice the store into different parts, and each part generates its own action and state objects. 99% of the time, we don’t use createReducer and createAction directly. Instead, we use createSlice.

  • createAsyncThunk

The createAsyncThunk method is used to create an asynchronous function, which is processed in reduce, and finally called with Dispatch in the business code. The basic process is no different from standard Redux. (Note that in createSlice we cannot use normal Reduce to handle asynchronous functions, we must use extraReducers to handle asynchronous functions.)

Six, how to use

1. Install

Yarn yarn add @reduxjs/ ToolkitCopy the code

2. Initialize state

interface InitialState {
  count: number;
}

const initialState: InitialState = {
  count: 0};Copy the code

3. Create a slice

export const getData = createSlice({
  name: "nameSpace",
  initialState,
  reducers: {},
  extraReducers: {},});Copy the code

4. Create asynchronous functions

const fetchData = createAsyncThunk("nameSpace/fetchData".async() = >await axios(someAPI));
Copy the code

5. Pass in an asynchronous function to update the status

const getData = createSlice({
  name: "nameSpace",
  initialState,
  reducers: {},
  extraReducers: {
    [fetchData.fulfilled.type]: (state: InitialState, action: PayloadAction<InitialState>) = >{ state.count = action.payload.count; ,}}});Copy the code

6. Create a Reducer

const rootReducer = combineReducers({ data: getData.reducer });
Copy the code

7. Create a warehouse

const store = configureStore({
  reducer: rootReducer,
  middleware: getDefaultMiddleware= > [...getDefaultMiddleware()],
  devTools: true});export default store;
Copy the code

The complete code is as follows:

import { createSlice, PayloadAction, createAsyncThunk, combineReducers, configureStore } from "@reduxjs/toolkit";
import axios from "axios";

interface InitialState {
  count: number;
}

const initialState: InitialState = {
  count: 0};export const fetchData = createAsyncThunk("nameSpace/fetchData".async() = >await axios(someAPI));

export const getData = createSlice({
  name: "nameSpace",
  initialState,
  reducers: {},
  extraReducers: {
    [fetchData.fulfilled.type]: (state: InitialState, action: PayloadAction<InitialState>) = >{ state.count = action.payload.count; ,}}});const rootReducer = combineReducers({ data: getData.reducer });

const store = configureStore({
  reducer: rootReducer,
  middleware: getDefaultMiddleware= > [...getDefaultMiddleware()], 
  devTools: true});export default store;
Copy the code

8. Use in business code:

import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
import { fetchData } from ".. /redux/slice";
import { useSelector } from ".. /redux/hooks";

const App: React.FC = () = > {
  const dispatch = useDispatch();
  const count = useSelector(({ data }) = > data);
  useEffect(() = >{ dispatch(fetchData()); } []);return <div>{count}</div>;
};

export default App;
Copy the code

Seven, finally

The writing is messy, as the study notes, later time will continue to optimize and supplement, if there is any mistake, thank you for correcting!