This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

Preface ๐ŸŒ‚

React is known to be a one-way data flow, so data transfer is relatively simple and the relationship between parents and children is relatively clear. However, if we want to do more complex data transfer, react alone is not enough. Therefore, we need to use Redux for more complex data transfer.

The following article will explain the redux workflow from beginner to advanced.

Ding! Start your Redux journey ~๐Ÿ‘

โ˜‚๏ธ 1. Basic knowledge

1. Brief introduction of Redux concept

React is a lightweight framework that doesn’t have a view layer. If you want to use it to pass data around, you pass it from parent to child, and then slowly move up the hierarchy.

But with Redux, if we want the data for a component, the data for that component will be stored in a store and managed by Redux. Then, through the Store, the data is passed step by step to the following components.

It is worth noting that we can view Redux as a combination of Reducer and Flux.

2. Redux workflow

Redux is essentially a data layer framework that puts all data in a Store. Let’s start with a picture:

You can see the store in the middle, where all the data is stored. If you look at the arrow down store, then each component is going to fetch data from the store.

We use an example to tease out the whole picture, as follows:

  • โ‘  There is one on the whole picturestoreIt holds all the data, i.eA common area for storing data;
  • โ‘ก Each component should be fromstoreTake the data inside;
  • โ‘ข Suppose we have a situation where we need to borrow books from a library. So we can takereact ComponentAfter that, the borrower has to go to the librarian to get the book. And the transfer of data in the process of borrowing a book can be regarded asAction CreatorsCan be understood as“What book do you want to borrow?”This sentence.
  • (4)Action CreaturesGo to thestore. At this point we’re going tostoreAs alibrarianHowever, there is no way for a librarian to remember the data status of all the books. Generally speaking, it requires a record book, you want to borrow what kind of books, so she will first check; Or if you want to return a book, she also checks to see where it needs to be put back.
  • โ‘ค At this time, it is necessary to followreducersGo to the correspondence. We can getreducersRegard as abookLibrarians use thisbookTo record the data you need. The administratorstorethroughreducerI know you should give it to the borrowerComponentsWhat kind of data.

๐ŸŽƒ 2, using Antd TodoList page layout

1. Use Antd in your project

Open the antDesign website ๐Ÿ‘‰ antD website portal to introduce it in the project first. The specific steps are as follows:

First, install ANTD. The command is as follows:

npm install antd --save
Copy the code

The second step is to introduce styles. The code is as follows:

import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
Copy the code

2. Use Antd to achieve the basic layout of TodoList

First, we create a new file in the project’s SRC folder named todolist.js. The specific code is as follows:

import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';

const data = [
  'Racing car sprays burning fuel into crowd.'.'Japanese princess to wed commoner.'.'Australian walks 100km after outback crash.'.'Man charged over missing wedding girl.'.'Los Angeles battles huge wildfires.',];class TodoList extends Component {
  render() {
    return (
      <div style={{marginTop: '10px', marginLeft: '10px'}} >
        <div>
          <Input placeholder="todo info" style={{ width: '300px' }} />
          <Button type="primary">submit</Button>
        </div>
        <List
          bordered
          dataSource={data}
          renderItem={item= > <List.Item>{item}</List.Item>} / ></div>)}}export default TodoList;
Copy the code

The browser displays:

Create a store in redux

(1) Create a store

Next we create a store in the project. Start by creating a new folder under the project’s SRC folder called Store. Next, under the Store folder, we create a new file named index.js. The specific code is as follows:

import { createStore } from "redux";
import reducer from './reducer';

const store = createStore(reducer);

export default store;
Copy the code

Next, go ahead and create a new file under the Store folder called Reducer.js. The specific code is as follows:

const defaultStore = {
  inputValue: ' '.list: []};export default (state = defaultStore, action) => {
  return state;
}
Copy the code

This completes the creation of the Store in the project.

(2) Use store in the project

After creating the Store, let’s use it in the project initially to see how it works. Add the data in store first, and transform the reducer. Js file in store. The specific code is as follows:

const defaultStore = {
  inputValue: '123'.list: [1.2]};export default (state = defaultStore, action) => {
  return state;
}
Copy the code

Todolist.js was then modified. The specific code is as follows:

import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store'; 

class TodoList extends Component {
  constructor(props) {
    super(props);
      this.state = store.getState()
  }
  render() {
    return (
      <div style={{marginTop: '10px', marginLeft: '10px'}} >
        <div>
          <Input placeholder={this.state.inputValue} style={{ width: '300px' }} />
          <Button type="primary">submit</Button>
        </div>
        <List
          bordered
          dataSource={this.state.list}
          renderItem={item= > <List.Item>{item}</List.Item>} / ></div>)}}export default TodoList;

Copy the code

The browser displays:

๐Ÿงต 3. Compile actions and Reducer – add functions

1. Content transformation of the main page

Next, we use action and Reducer to pass data back and forth from this component. First, let’s transform the todolist.js file. The specific code is as follows:

import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store'; 

class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = store.getState()
    this.handleInputChange = this.handleInputChange.bind(this)
    this.handleStoreChange = this.handleStoreChange.bind(this)
    this.handleBtnClick = this.handleBtnClick.bind(this)
    store.subscribe(this.handleStoreChange)
  }
  render() {
    return (
      <div style={{marginTop: '10px', marginLeft: '10px'}} >
        <div>
          <Input
            value={this.state.inputValue}
            placeholder="todo info"
            style={{ width: '300px', marginRight: '10px'}}
            onChange={this.handleInputChange}
          />
          <Button type="primary" onClick={this.handleBtnClick}>submit</Button>
        </div>
        <List
          style={{marginTop: '10px', width: '300px'}}
          bordered
          dataSource={this.state.list}
          renderItem={item= > <List.Item>{item}</List.Item>} / ></div>)}handleInputChange(e) {
    In React, action is the form of an object
    // Type tells react to change the input value for me. This value is the following value, e.target.value
    const action = {
      type: 'change_input_value'.value: e.target.value
    }
    store.dispatch(action)
    // console.log(e.target.value)
  }

  handleStoreChange() {
    // Call the store. GetState method and fetch the data from the store again.
    // Then call setState to replace the current store data
    this.setState(store.getState())
  }

  handleBtnClick() {
    const action = {
      type: 'add_todo_item'
    }
    store.dispatch(action)
  }
}

export default TodoList;
Copy the code

Let’s examine the above code. First, each action is bound to the corresponding event, and then, within the event, the action is created. In the case of the created action, it tells React to help the action change the value to which it is bound.

Finally, the action is done, and its data needs to be stored in a store. The action is then processed via store.dispatch(Action), passing the action’s data to the store.

Change the data in the action

The initial value of the action is fixed. However, reducer is needed when we want to modify the value in the action. Now, let’s modify the reducer. Js file so that the input box can input values freely and add the list after clicking the submit button. The specific code is as follows:

const defaultStore = {
  inputValue: '123'.list: [1.2]};Reducer can receive state, but must not modify state
const reducer = (state = defaultStore, action) = > {
  if (action.type === 'change_input_value') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }
  if (action.type === 'add_todo_item') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.list.push(newState.inputValue);
    newState.inputValue = ' ';
    console.log(newState);
    return newState;
  }
  return state;
}
export default reducer;
Copy the code

3. Store data transformation

Next, let’s take a look at the contents of index.js in the Store folder. We need to make a simple transformation of it, the specific code is as follows:

import { createStore } from "redux";
import reducer from './reducer';

const store = createStore(
  reducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

export default store;
Copy the code

In addition to the reducer, we pass window.__redux_devTools_extension__ &&window.__redux_devTools_extension__ () and call this method.

Finally, let’s take a look at the browser display:

๐Ÿงถ 4. Use Redux to achieve the deletion function of TodoList

1. Bind components to events

Now that we’ve done the add function, let’s move on to the delete function, which deletes the data for each click. Todolist.js: todolist.js: todolist.js: todolist.js: todolist.js: todolist.js: todolist.js

import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store'; 

class TodoList extends Component {
  constructor(props) {
    // Omit the previous code here
  }
  render() {
    return({/* omit the above code */}
        <List
          style={{marginTop: '10px'.width: '300px'}}
          bordered
          dataSource={this.state.list}
          renderItem={(item, index) = > <List.Item onClick={this.handleItemDelete.bind(this, index)} >{item}</List.Item>}
        />
      </div>
    )
  }

  // Omit the previous code here
  
  handleItemDelete(index) {
    const action = {
      type: 'delete_todo_item', index } store.dispatch(action); }}export default TodoList;
Copy the code

2. Data communication was conducted in reducer

Then, we communicate the data in reducer. Js file. The specific code is as follows:

const defaultStore = {
  inputValue: '123'.list: [1.2]};Reducer can receive state, but must not modify state
const reducer = (state = defaultStore, action) = > {
  // Omit the previous code here
    
  if (action.type === 'delete_todo_item') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.list.splice(action.index, 1);
    return newState;
  }
  return state;
}
export default reducer;
Copy the code

Now, let’s take a look at the browser display:

๐Ÿ‘“ logical induction

1. Split ActionTypes

In todolist.js above, you can see that we’re going to be doing actions a lot. At the same time, if we make a slight mistake in type, the sorting process is always difficult to locate.

So, one of the things we’re going to do is split ActionTypes.

First, we’ll add a new file in the Store folder called actiontypes.js. The specific code is as follows:

export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';
Copy the code

Second, modify the content under Todolist.js. The specific code is as follows:

import {
  CHANGE_INPUT_VALUE,
  ADD_TODO_ITEM,
  DELETE_TODO_ITEM
} from './store/actionTypes'

class TodoList extends Component {

  handleInputChange(e) {
    const action = {
      type: CHANGE_INPUT_VALUE,
      value: e.target.value
    }
    store.dispatch(action)
  }

  handleStoreChange() {
    this.setState(store.getState())
  }

  handleBtnClick() {
    const action = {
      type: ADD_TODO_ITEM
    }
    store.dispatch(action)
  } 

  handleItemDelete(index) {
    const action = {
      type: DELETE_TODO_ITEM, index } store.dispatch(action); }}export default TodoList;
Copy the code

Finally, reform the reducer.js file. The specific code is as follows:

import {
  CHANGE_INPUT_VALUE,
  ADD_TODO_ITEM,
  DELETE_TODO_ITEM
} from './actionTypes';

const defaultStore = {
  inputValue: '123'.list: [1.2]};const reducer = (state = defaultStore, action) = > {
  if (action.type === CHANGE_INPUT_VALUE) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }
  if (action.type === ADD_TODO_ITEM) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.list.push(newState.inputValue);
    newState.inputValue = ' ';
    console.log(newState);
    return newState;
  }
  if (action.type === DELETE_TODO_ITEM) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.list.splice(action.index, 1);
    return newState;
  }
  return state;
}
export default reducer;
Copy the code

By integrating change_input_value, add_todo_item, and delete_todo_item into actiontypes.js, we can make it easier to debug if we get a letter wrong.

2. Use actionCreator to create action

As you can see from todolist.js above, for several bound events, we always have to create actions frequently. Repetitive actions are the most taboo thing in programs. Therefore, we need to use Action Creator to unify the management of actions and make the logic more unified and complete.

First, we will create a new file in the Store folder called actionActionine.js. The specific code is as follows:

import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from "./actionTypes";

export const getInputChangeAction = (value) = > ({
  type: CHANGE_INPUT_VALUE,
  value: value
});

export const getAddItemAction = (value) = > ({
  type: ADD_TODO_ITEM
});

export const getDeleteItemAction = (index) = > ({
  type: DELETE_TODO_ITEM,
  index: index
});
Copy the code

Moving on, let’s transform Todolist.js. The specific code is as follows:

import { getInputChangeAction, getAddItemAction, getDeleteItemAction } from './store/actionCreators'

class TodoList extends Component {

  handleInputChange(e) {
    const action = getInputChangeAction(e.target.value);
    store.dispatch(action)
  }

  handleBtnClick() {
    const action = getAddItemAction();
    store.dispatch(action)
  } 

  handleItemDelete(index) {
    constaction = getDeleteItemAction(index); store.dispatch(action); }}export default TodoList;
Copy the code

The final logic is more unified by separating the actions from the action into actionCreators. Js.

๐Ÿ‘” some summary of Redux

At this point, we sum up some of the above knowledge points as follows

1. Three principles of Redux design and use

The design and use of Redux follows three principles:

  • storeMust be unique ๐Ÿ‘‰ that is, there must be only one in the entire applicationstore ๏ผ›
  • onlystoreBe able to change your content ๐Ÿ‘‰ i.estorenotreducerTo renew, butstoreGet thereducerAfter the data, their own data for an update; So let’s go back to the topreducer.jsFile,reactIn, is not allowedState. inputValue === a valueSomething like that, which means you can’t assign it directly.
  • ReducerMust be pure function ๐Ÿ‘‰ so calledPure functionsThat is, given a fixed input, there must be a fixed output without any side effects. Let’s go back to the one above usreducer.jsThe document, as you can see,stateIt’s fixed,actionIs also fixed, so the final returnnewStateNature means fixed.

Redux’s core API

Let’s review some of Redux’s core apis. Take a look at the picture below:

Now let’s review what these core apis do. Details are as follows:

  • CreateStore – can help us create onestore ๏ผ›
  • Store. Dispatch — —dispatchMethods to help us distributeactionAt the same time, thisactionWill pass tostore ๏ผ›
  • Store. GetState — —getState Methods help us get all the data;
  • Store. The subscribe — —subscribeHelp us subscribestoreChange, as long asstoreChange,store.subscribeThe received callback function is executed.

๐Ÿ‘ 7. Separation of advanced components

1. Separation of UI components and container components

In the code above, we are almost done with TodoList. However, did you notice that in todolist.js, the rendering of the page and the logic of the page are put together?

In real development, we tend to separate UI components and container components directly. The UI component is dedicated to rendering the page, while the container component is responsible for the logic of the page.

So let’s break it up. First, we will now add a new file in the SRC folder named todolistui.js. The specific code is as follows:

import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';

class TodoListUI extends Component {
    render() {
        return (
            <div style={{ marginTop: '10px', marginLeft: '10px' }}>
                <div>
                    <Input
                        value={this.props.inputValue}
                        placeholder="todo info"
                        style={{ width: '300px', marginRight: '10px'}}onChange={this.props.handleInputChange}
                    />
                    <Button type="primary" onClick={this.props.handleBtnClick}>submit</Button>
                </div>
                <List
                    style={{ marginTop: '10px', width: '300px'}}bordered
                    dataSource={this.props.list}
                    renderItem={(item, index) = > <List.Item onClick={()= > { this.props.handleItemDelete(index) }}>{item}</List.Item>} / ></div>)}}export default TodoListUI;
Copy the code

Moving on, let’s modify the contents of the todolist.js file. The specific code is as follows:

import React, { Component } from 'react';

import store from './store';
import { getInputChangeAction, getAddItemAction, getDeleteItemAction } from './store/actionCreators';
import TodoListUI from './TodoListUI';

class TodoList extends Component {
    constructor(props) {
        super(props);
        this.state = store.getState()
        this.handleInputChange = this.handleInputChange.bind(this)
        this.handleStoreChange = this.handleStoreChange.bind(this)
        this.handleBtnClick = this.handleBtnClick.bind(this)
        this.handleItemDelete = this.handleItemDelete.bind(this)
        store.subscribe(this.handleStoreChange)
    }
    render() {
        return (
            <TodoListUI
                inputValue={this.state.inputValue}
                list={this.state.list}
                handleInputChange={this.handleInputChange}
                handleBtnClick={this.handleBtnClick}
                handleItemDelete={this.handleItemDelete}
            />)}handleInputChange(e) {
        const action = getInputChangeAction(e.target.value);
        store.dispatch(action)
    }

    handleStoreChange() {
        this.setState(store.getState())
    }

    handleBtnClick() {
        const action = getAddItemAction();
        store.dispatch(action)
    }

    handleItemDelete(index) {
        constaction = getDeleteItemAction(index); store.dispatch(action); }}export default TodoList;
Copy the code

As you can see, we pulled out the content of the page and put it in the todolistui.js file to do only rendering. Thus, we have successfully separated the UI component from the logical component.

Stateless components

Now that we have the UI component, let’s look at another component, the stateless component. The so-called stateless component is the whole page no logic, only a render function, we can call it a stateless component.

How w do you define stateless components?

We can define a function that takes a parameter, props. The todolistui.js file contains the following code:

import React from 'react';
import { Input, Button, List } from 'antd';

const TodoListUI = (props) = > {
    return (
        <div style={{ marginTop: '10px', marginLeft: '10px' }}>
            <div>
                <Input
                    value={props.inputValue}
                    placeholder="todo info"
                    style={{ width: '300px', marginRight: '10px'}}onChange={props.handleInputChange}
                />
                <Button type="primary" onClick={props.handleBtnClick}>submit</Button>
            </div>
            <List
                style={{ marginTop: '10px', width: '300px'}}bordered
                dataSource={props.list}
                renderItem={(item, index) = > <List.Item onClick={()= > { props.handleItemDelete(index) }}>{item}</List.Item>} / ></div>)}export default TodoListUI;
Copy the code

When a normal function only has the render function, we can simply replace it with a stateless component. So why do I make this substitution?

If we change it to just one function, then the program only needs to run that function, and that’s all it needs to do. In other words, if we use class, then the object behind the class has a lot of life cycle functions and so on, which is not so pure. Therefore, we define stateless components in such a way as to make components more pure.

๐ŸŽฉ Redux initiates an asynchronous request

1. Send asynchronous request data in Redux

Often in real projects, we need to interface data with back-end requests and send AJAX requests. React requests to the backend interface.

First we request data under todolist.js. The specific code is as follows:

import { getInputChangeAction, getAddItemAction, getDeleteItemAction, initListAction } from './store/actionCreators';

class TodoList extends Component {
	componentDidMount() {
        axios.get('./list.json').then((res) = > {
            const data = res.data;
            constaction = initListAction(data); store.dispatch(action); }}})Copy the code

Next, modify the actiontypes.js code. Details are as follows:

export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';
export const INIT_LIST_ACTION = 'init_list_action';
Copy the code

Moving on, we packaged the action in actionActionine.js. The specific code is as follows:

import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION } from "./actionTypes";

export const getInputChangeAction = (value) = > ({
  type: CHANGE_INPUT_VALUE,
  value: value
});

export const getAddItemAction = (value) = > ({
  type: ADD_TODO_ITEM
});

export const getDeleteItemAction = (index) = > ({
  type: DELETE_TODO_ITEM,
  index: index
});

export const initListAction = (data) = > ({
  type: INIT_LIST_ACTION,
  data: data
})
Copy the code

Finally, modify the reducer.js code. The specific code is as follows:

import {
  CHANGE_INPUT_VALUE,
  ADD_TODO_ITEM,
  DELETE_TODO_ITEM,
  INIT_LIST_ACTION
} from './actionTypes';

const defaultStore = {
  inputValue: '123'.list: [1.2.3]};Reducer can receive state, but must not modify state
const reducer = (state = defaultStore, action) = > {
  if (action.type === CHANGE_INPUT_VALUE) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }
  if (action.type === INIT_LIST_ACTION) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.list = action.data;
    return newState;
  }
  if (action.type === ADD_TODO_ITEM) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.list.push(newState.inputValue);
    newState.inputValue = ' ';
    console.log(newState);
    return newState;
  }
  if (action.type === DELETE_TODO_ITEM) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.list.splice(action.index, 1);
    return newState;
  }
  return state;
}
export default reducer;
Copy the code

Thus, we have implemented the AJAX request via AXIos, please let it get the data.

2. Redux-thunk middleware

(1) What problems are solved

In the example above, we successfully made a request for the interface’s data. The above case is a relatively simple example, but often in the actual scene we encounter, are more complex examples.

Therefore, we want to move it to another file for management when we have asynchronous requests or very complex logic.

This is where redux-Thunk middleware is used for problem solving. How to use redux-Thunk middleware?

(2) How to use it

Step 1: Install redux-Thunk. Specific commands are as follows:

npm i redux-thunk -D
Copy the code

Step 2: Introduce redux-thunk. Redux-devtools is often used to debug the project store during actual debugging. But what if we were to introduce both Redux-DevTools and redux-Thunk middleware? In the store | index. Js file for processing. The specific code is as follows:

The compose function comes from redux
import { createStore, applyMiddleware, compose } from "redux";
import reducer from './reducer';
import thunk from 'redux-thunk';

const composeEnhancers = 
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?   
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;

const enhancer = composeEnhancers(
  applyMiddleware(thunk)
);

const store = createStore(
  reducer,
  enhancer
);

export default store;
Copy the code

With this form of coding, our store supports Both DevTools for Windows, which means we can debug the store, and redux-thunk.

Step 3: Pull out the asynchronous logic. Let’s start by modifying the todolist.js code. Details are as follows:

import { getTodoList, getInputChangeAction, getAddItemAction, getDeleteItemAction } from './store/actionCreators';

class TodoList extends Component {
	componentDidMount() {
        // Action is a function
        const action = getTodoList();
        // Only with thunk can action be passed as a functionstore.dispatch(action); }}Copy the code

Next, modify the actionactionine.js code. The specific code is as follows:

// getTodoList is a function
// A function generated in this form can receive the dispatch method directly
export const getTodoList = () = > {
  return (dispatch) = > {
    axios.get('./list.json').then((res) = > {
      const data = res.data;
      // Action is an object
      constaction = initListAction(data); dispatch(action); }}})Copy the code

Let’s explain the above two pieces of code as follows:

After configuring the Redux-Thunk environment, it allows us to write asynchronous code in the action! Why do you say that?

  • Before we were creatingactionCan only be oneJS objectAnd now, when usedredux-thunkAnd then, even thoughgetTodoList()The returnednotAn objectbutA function can also passstore.dispatch()To send the function tostore.
  • Then why can befunctionWhat about sending it out? Because of using itredux-thunk ใ€‚

Moving on, we’ll talk about specific implementation steps ๐Ÿ‘‡ :

  • First, letTodoList.jsIn thestoreTo performactionFunction. And thisactionThe delta function comes from thetaactionCreators.jsIn thegetTodoList() ใ€‚
  • forgetTodoList()Say, what it has to do is goRequest JSON dataandGet json data.
  • And once you’ve got the data, then you have to changestoreThe data inside, then you have to create one firstactiontheactionProvide forstore.dispatch()Call. But,store.dispatch()How do you get it? It will be automatically received in the function that we returnstore.dispatch()Methods. So, just passdispatch(action)That will beactionJust hand out the pies.
  • In other words,redux-thunkIt makes us createactionOr supportactionWhen, is onefunctionIn the form.

(3) Why redux-thunk?

After reading the above explanation, you should know the magic of Redux-thunk. So why redux-thunk? ๐Ÿ‘‡

If asynchronous functions are used in the life cycle of a component, the logic of the component becomes more complex and the content of the component becomes more and more. As a result, complex asynchronous logic is often split off and managed separately. Now, with the help of the Redux-Thunk middleware, we split the asynchronous logic into actionActionine.js for separate management. As a result, the code is more standardized and unified.

(4) What is Redux-Thunk middleware?

With that in mind, let’s go back to the middleware roots and talk about the principles behind the Redux-Thunk middleware.

The so-called middleware is definitely the middle of who and who. Let’s start with a picture:

The middle of the Redux middleware is between the Action and the Store.

As we said earlier, in Redux, an action can only be an object, and because it is an object, it is sent directly to the store. Now, when we use redux-thunk, action can be a function. So why can it be a function?

As you can see from the above figure, the Action sends the data to the Store via dispatch. And there’s a dispatch method between an action and a store, so middleware is essentially a wrapper and an upgrade to the Dispatch method.

For the original Dispatch method, it would receive a JS object and pass it to the Store.

But if we pass a function, then the dispatch is upgraded. Instead of passing functions directly to the Store, Dispatch executes the corresponding functions through the Redux-Thunk middleware and calls the Store when it needs to be called.

๐Ÿ’ผ Other middleware for Redux

1, the story – the logger

There are a lot of middleware in Redux. For example, the Redux-Logger can log every time an action is sent. So how does it record?

Every time it calls an action, it passes the action to the Store through the Dispatch method, and then we can upgrade the Dispatch so that not only does the Dispatch pass the action to the Store, And before each delivery, we printed it console.log, so we wrote a middleware for Redux-Logger that printed the action to our console when we sent it.

2, the story – saga

(1) What is Redux-saga

In today’s projects, the popular middleware not only includes Redux-Thunk, Redux-Logger, but also Reudx-Saga.

Reudx-saga is also a middleware solution to asynchronous problems in React. Unlike Redux-Thunk, redux-Thunk uses asynchronous operations in actions. The idea behind Redux-Saga is to separate asynchronous logic and manage it in a separate file. How to use the redux-Saga middleware?

(2) How to use Redux-Saga

We upgraded the TodoList component above. First is the store | index. Js file. The specific code is as follows:

import { createStore, applyMiddleware, compose } from "redux";
import reducer from './reducer';
import createSagaMiddleware from 'redux-saga';
import todoSagas from './sagas';

const sagaMiddleware = createSagaMiddleware()
const composeEnhancers = 
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?   
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(sagaMiddleware(sagaMiddleware));

const store = createStore(reducer, enhancer);
sagaMiddleware.run(todoSagas);

export default store;
Copy the code

In this file, it’s all about getting the basic configuration right. The main points to note here are:

  • The introduction ofcreateSagaMiddleware ๏ผ›
  • And then useconst sagaMiddleware = createSagaMiddleware()To introduce it;
  • useapllyMiddlewareTo use this middleware;
  • After using the middleware, we created thesaga.js ใ€‚

Next we create saga.js in the Store folder. The specific code is as follows:

import { takeEvery, put } from 'redux-saga/effects';
import { initListAction } from './actionCreators';
import { GET_INIT_LIST } from './actionTypes';
import axios from 'axios';

function getInitList() {
  try {
    const res = yield axios.get('./list.json');
    const action = initListAction(res.data);
    yield put(action);
  } catch (e) {
    console.log('List. json network request failed'); }}function* mySaga() {
  // Use takeEvery to capture every action sent out
  yield takeEvery(GET_INIT_LIST, getInitList);
}

export default mySaga;
Copy the code

There are a few things to note about saga.js:

  • insaga.jsInside, you have to export onegeneratorFunction, and inside this function, we’ve written some logic. The logic is, when we receiveactionType isGET_INIT_LIST“, then we will executegetInitListThis method.
  • getInitList()A method is a function that will fetch the data for us, and then create a new one from that dataactionAnd put thisactionthroughyield put(action)The way, sent tostore ใ€‚

Now let’s look at actiontypes.js. The specific code is as follows:

// CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION
export const GET_INIT_LIST = 'get_init_list';
Copy the code

Next, we go to Todolist.js. The specific code is as follows:

import { getInputChangeAction, getAddItemAction, getDeleteItemAction, getInitList } from './store/actionCreators';

class TodoList extends Component {
    
    // omit n content here
    
    componentDidMount() {
        constaction = getInitList(); store.dispatch(action); }}export default TodoList;
Copy the code

Finally, the store | actionCreators. Js. The specific code is as follows:

import { GET_INIT_LIST, CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION } from "./actionTypes";

// omit n content here

export const getInitList = () = > ({
  type: GET_INIT_LIST
});
Copy the code

In todolist.js, we create an action and send it to the store.

๐Ÿ›ต ten, React – story

1. What is React-redux

After react, we learned redux. So if you combine them, what is react-redux?

In fact, it’s a third-party module that makes it easier to use Redux in React.

Use of React-redux

(1) Install react-redux

Similarly, let’s look at the use of React-Redux using the TodoList component as an example. First, create a react project and install react-redux. Specific commands are as follows:

npm install react-redux
Copy the code

(2) Project catalog

Let’s start with the project catalog. The details are as follows:

(3) Core content

The first step is to mount the TodoList component to the page. Under the SRC | index. Js file content is as follows:

import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
import { Provider } from 'react-redux';
import store from './store';


const App = (
  // Indicates that all components of the Provider have the ability to obtain the store
  <Provider store={store}>
    <TodoList />
  </Provider>
)

ReactDOM.render(App, document.getElementById('root'));

Copy the code

Provider is the first core API provided by React. It is designed to show that all components in the Provider have the ability to access the Store.


The second step is to write the SRC | TodoList. Js. The specific code is as follows:

import React from 'react';
import { connect } from 'react-redux';

const TodoList = (props) = > {
  const { inputValue, list, changeInputValue, handleClick, handleDelete } = props;
  return (
    <div>
      <div>
        <input value={inputValue} onChange={ changeInputValue} / >
        <button onClick={ handleClick} >submit</button>
      </div>
      <ul>
        {
          list.map((item, index) => {
            return <li onClick={handleDelete} key={index}>{ item }</li>})}</ul>
    </div>)}const mapStateToProps = (state) = > {
  return {
    inputValue: state.inputValue,
    list: state.list
  }
}

// store, dispatch, props
const mapDispatchToProps = (dispatch) = > {
  return {
    changeInputValue(e) {
      const action = {
        type: 'change_input_value'.value: e.target.value
      };
      // console.log(action.value)
      dispatch(action);
    },
    handleClick() {
      const action = {
        type: 'add_item'
      }
      dispatch(action);
    },
    handleDelete(){}}}// Let TodoList connect to store
// TodoList is a UI component. Connect combines this UI component with the preceding business logic
export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
Copy the code

In the code above, we notice connect in React-Redux.

Who is connecting with whom? TodoList connects to Store. To connect them, they need a mapping, and that mapping is inside mapStateToProps.

In mapStateToProps, state refers to the data in store. If the data in store is mapped to props, then we can use this.props. XXX to get the data in store.


Step 3 Create a Reducer. In SRC | store | reducer, js, under specific code is as follows:

const defaultState = {
  inputValue: ' '.list: []}export default (state = defaultState, action) => {
  if (action.type === 'change_input_value') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }
  if (action.type === 'add_item') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.list.push(newState.inputValue);
    newState.inputValue = ' ';
    return newState;
  }
  return state;
}
Copy the code

Put the data in the store into the reducer for recording.


Step 4, pass the Reducer to the store. In SRC | store | index, js, under specific code is as follows:

import { createStore } from 'redux';
import reducer from './reducer';

const store = createStore(reducer);

export default store;
Copy the code

We make a deep copy of the content stored in the Reducer and send it back to the store. In this way, a closed loop of data transmission is formed.


Finally, let’s take a look at the browser display:

React-redux is much more intuitive and concise to use than middleware. In a real world project, either redux middleware or React-Redux is worth using for state management.

It is important to note that redux middleware and React-Redux have different points in their use. As for which type to use in a project, it depends on the current project scenario.

๐Ÿšฆ 11. Conclusion

In the previous article, we covered three principles for Redux design and use, as well as some of the core apis in Redux. In addition, we also learned about the middleware of Redux, Redux-Thunk and Redux-Saga. You also learned about another aspect of state management, React-redux.

That’s it for Redux! I wonder if you have a new understanding of Redux?

๐Ÿฃ Egg One More Thing

Previous recommendation

๐Ÿ‘‰ Use React to implement todoList

๐Ÿ‘‰ React on the surface? Five knowledge points take you to comb the advanced knowledge

) Introduction

  • Pay attention to the public account Monday laboratory, the first time to pay attention to quality articles, more “offer to come” interview column for you to unlock ~
  • If you think this article is helpful to you, you might as well like to support yo ~~๐Ÿ˜‰
  • That’s all for this article! See you next time! ๐Ÿ‘‹ ๐Ÿ‘‹ ๐Ÿ‘‹