create by jsliang on 2019-3-26 09:26:53

Recently revised in 2019-4-7 03:04:01

If you think this is a good article, please send me astar, friendsstarIs my motivation to continue to update!Making the address


Hello guys, due to jsliang’s refactoring of the document library, some links in this article may be broken, and Jsliang lacks the energy to maintain the old articles on the Nuggets side. Sorry about that. If you need to get the latest article, please click on the GitHub address above and go to the document library to view the adjusted article.


  • React series article code address

A directory

What’s the difference between a free front end and a salted fish

directory
A directory
The preface
Initialize the project
Use Ant Design
Five use Redux
Six Redux discovery
 6.1 Redux plug-in
 6.2 Redux knowledge points
Seven Redux explore
 7.1 Input Input data
 7.2 Button submits data
 7.3 Deleting the TodoItem List Item
Optimization: Extract ActionType
Optimization: Extract the entire action
Optimization: UI components and container components
Optimization: stateless components
Twelve-ending: Call Axios, redux-base completes
Step 13: Redux middleware
Level 14: Redux-Thunk middleware for Ajax request management
Level 15: Redux-Saga middleware for Ajax request management
16 Advanced: React-redux
Seventeen summary

The preface

Returns the directory

Disclaimer: This series of articles is based on the React video and my personal understanding:

  • React 16.4 Developing simple Book Projects from Scratch

This Demo is based on ReactDemoOne to upgrade Redux. We will also introduce the middleware Redux-Thunk and Redux-Saga, and finally use React-Redux for project refactoring.

So, for those of you who didn’t read the first one, check it out:

  • React Demo One – TodoList

If you want to read this source code together, you can go to the following address to download:

  • React series source address

Note that this code is in the TodoListUpgrade directory, and it has four folders, corresponding to:

  • Redux-base — Records Redux Base content
  • Redux-thunk — Based on redux-baseredux-thunkMiddleware configuration
  • Redux-saga — Build on redux-Baseredux-sagaMiddleware configuration
  • React-redux — perform on TodoListreact-reduxreinventing

Redux-base redux-base redux-base redux-base redux-base

Initialize the project

Returns the directory

Get the code from the Simpify directory in the React series, Copy it to redux-Base, and change the App to TodoList for TodoList minor modifications.

Let’s start with:

  1. src/index.js
Details of the code
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';

ReactDOM.render(<TodoList />, document.getElementById('root'));
Copy the code
  1. src/App.js TodoList.js
Details of the code
import React, { Component } from 'react';
import './index.css';

class TodoList extends Component {
  render() {
    return (
      <div className="App">
        Hello TodoList!
      </div>); }}export default TodoList;
Copy the code
  1. src/index.css
Details of the code
/* Not yet written */
Copy the code

At this point, we run NPM run start on the terminal, and the result is displayed:

Use Ant Design

Returns the directory

  • Ant Design website: Ant. Design /index-cn

Let’s start using Ant Design in a TodoList project:

  • Installation:npm i antd -S
  • Use:
  1. src/TodoList.js
Details of the code
import React, { Component } from 'react'; // Introduce React and its Component
import './index.css'; / / the introduction of the index. The CSS
import { Input, Button, List } from 'antd'; // 1. Introduce a list of ANTds
import 'antd/dist/antd.css'; // 2. Introduce antD style

// 3. Define data
const data = [
  'This is very, very, very long and incredibly long but it's the first TodoList that flows smoothly.'.'This is very, very, very long and incredibly long but it's a neat second TodoList.'.'This is a very, very, very long and incredibly long but it's a very smooth third TodoList'.'This is a very, very, very long and incredibly long but it's a very smooth fourth TodoList',];class TodoList extends Component {
  render() {
    return( <div className="todo"> <div className="todo-title"> <h1>TodoList</h1> </div> {/* 4. <div className="todo-action"> <Input placeholder='todo' className="todo-input" /> <Button </Button> </div> {/* 5. } <div className="todo-list"> <List size="large" bordered dataSource={data} renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)} /> </div> </div> ); } } export default TodoList;Copy the code
  1. src/index.css
Details of the code
.todo {
  width: 1000px;
  margin: 20px auto 0;
  padding: 30px;
  border: 1px solid #ccc;
  border-radius: 10px;
}
.todo-title {
  text-align: center;
}
.todo-action .todo-input {
  width: 200px;
}
.todo-action .todo-submit {
  margin-left: 10px;
}
.todo-list {
  margin-top: 30px;
}
Copy the code

Here, we refer to Ant Design as follows:

  1. Introduce antD Input, Button, List components
  2. Introduce the antD style
  3. Define the data
  4. Use Input and Button components
  5. Using the List component

The page is displayed as:

Five use Redux

Returns the directory

I think it’s worth experimenting with Redux before we get into it:

  • Install the story:npm i redux -S
  • Here’s how Redux works:

Create a store directory in the SRC directory to store Redux data. This directory contains reducer. Js and index.js files

First, we write a reducer.js file, which defines and processes data:

  1. src/store/reducer.js
Details of the code
1. We define a data defaultState
const defaultState = {
  inputValue: ' '.todoList: [
    'This is very, very, very long and incredibly long but it's the first TodoList that flows smoothly.'.'This is very, very, very long and incredibly long but it's a neat second TodoList.'.'This is a very, very, very long and incredibly long but it's a very smooth third TodoList'.'This is a very, very, very long and incredibly long but it's a very smooth fourth TodoList']},// 2. We finally export the data defaultState as state
export default (state = defaultState, action) => {
  return state;
}
Copy the code

Next, we write the index.js file, which creates the data warehouse through the createStore method and exports it to Todolist.js.

  1. src/store/index.js
Details of the code
import { createStore } from 'redux'; // 3. We refer to the createStore library in redux
import reducer from './reducer'; // 4. We refer to the reducer.js data

// 5. We build a data storage repository through reducer, a method provided by Redux
const store = createStore(reducer);

// 6. We export store
export default store;
Copy the code

Finally, we reference store/index.js in todolist.js and use it in the list, as well as print out the data that store passed to us:

  1. src/TodoList.js
Details of the code
import React, { Component } from 'react'; // Introduce React and its Component
import './index.css'; / / the introduction of the index. The CSS
import { Input, Button, List } from 'antd'; // Import antD components
import 'antd/dist/antd.css'; // Introduce the antD style
import store from './store'; // 7. Introduce store, you can understand to provide data for store. ./store is short for./store/index.js

class TodoList extends Component {

  // 8. Get data from constructor using the store.getState() method and assign state to it
  constructor(props) {
    super(props);
    // 9. We try to print store.getState() in Console
    console.log(store.getState());
    this.state = store.getState();
  }

  render() {
    return<div className="todo"> <h1>TodoList</h1> </div> {/* Use Input, Button component */} <div className="todo-action"> <Input placeholder='todo' className="todo-input" /> <Button type="primary" Submit the className = "todo - submit" > < / Button > < / div > {/ * * use the List component /} {/ * 10. Replace the original data with state todoList */} <div className="todo-list"> < list size="large" bordered dataSource={this.state.todoList} renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)} /> </div> </div> ); } } export default TodoList;Copy the code

At this point, we looked at the Chrome console and page and saw that it did work:

So now that we’re done querying the data in Redux, how do we modify the data in Redux, and what is Redux? Let’s do it one by one.

Six Redux discovery

Returns the directory

  • Redux official website: Link
  • Redux Chinese Volume: Links

If you feel that you read the brochure or the official website to understand more transparent, please check, the following views are for reference only.

6.1 Redux plug-in

Returns the directory

  1. Installation: Scientific Internet to find the corresponding Chrome plug-in, or Baidu download a, or throughnpm install --save-dev redux-devtoolsInstall its developer tools.
  2. Use:
    1. Close the browser, reopen it, and open the console again (F12) to the Redux bar, prompting you that you have not yet installed the code
    2. Go to index.js to install the code.
    3. The Redux plug-in is installed when data is found in state.

src/store/index.js

Details of the code
import { createStore } from 'redux';
import reducer from './reducer';

// If you have the Redux tool installed, you can use it directly here
const store = createStore(
  reducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

export default store;
Copy the code

6.2 Redux knowledge points

Returns the directory

React defines itself as “a JavaScript library for building the user interface.”

Therefore, when we use React, if the sibling components need to communicate, such as the dark circle on the left in the image above, to send data to the circle on the top (first row), we need to go around a lot, which makes data communication very difficult.

Redux was created to compensate for this cumbersome method of communication by creating a central mechanism for communicating between components. Hence the scheduling scheme on the right in the figure above.

So how does Redux work? We analyze it through the following picture:

In the figure above, we assume:

  • Blue (React Component) : Book borrower
  • Action Creators: Check out the book
  • Orange (Store) : Librarian
  • Purple (Reducers) : system

Its process can be understood as: first, the borrower goes to the front desk (borrowing the book action) to apply for borrowing the book from the librarian, the librarian helps it find the book materials in the system, and then gets the computer to return the information, and finally tells him where to borrow/how to use the book.

In normal terms, when the React Components needs to call data, it sends a request to the Action Creators, who inform the Manager of the Store. The manager then looks for Reducers and reports the information back to the component.

In doing so, we use some of Redux’s common/core apis:

  1. createStore: create the store
  2. store.dispatchDistribute the action:
  3. store.getState: Gets all data content from the store
  4. store.subscribe: Monitors store changes. If store changes, this method is executed

Let’s take this a step further by using Input data, Button submission data, and removing the TodoItem list item.

Redux data modification

Returns the directory

Let’s start by explaining Redux data modification with Input data, Button submission data, and removing TodoItem list items.

7.1 Input Input data

Returns the directory

  1. src/TodoList.js
Details of the code
import React, { Component } from 'react'; // Introduce React and its Component
import './index.css'; / / the introduction of the index. The CSS
import { Input, Button, List } from 'antd'; // Import antD components
import 'antd/dist/antd.css'; // Introduce the antD style
import store from './store'; // Import store, you can understand to provide data for store. ./store is short for./store/index.js

class TodoList extends Component {

  // Get data from constructor using the store.getState() method and assign the value to state
  constructor(props) {
    super(props);
    // We try to print store.getState() in Console
    // console.log(store.getState());
    this.state = store.getState();
    
    // 2. define handleInputChange
    this.handleInputChange = this.handleInputChange.bind(this);

    // 7. Bind the method handleStoreChange to handle the data returned by Redux
    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);
  }

  render() {
    return<div className="todo"> <h1>TodoList</h1> </div> {/* Use Input, Button component */} <div } <Input placeholder='todo' className="todo-input" className="todo-input" className="todo-input" Value ={this.state.inputValue} onChange={this.handleInputChange} /> <Button type="primary" className="todo-submit" > Submit </Button> </div> {/* Use the List component */} {/* replace the original data with state todoList */} <div className="todo-list"> <List size="large" bordered dataSource={this.state.todoList} renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)} /> </div> </div> ); } handleInputChange handleInputChange(e) {// 4. Const action = {type: 'change_input_value', value: e.target.value} store.dispatch(action); HandleStoreChange () {this.setState(store.getState()); } } export default TodoList;Copy the code
  1. src/store/reducer.js
Details of the code
// Define a data defaultState
const defaultState = {
  inputValue: ' '.todoList: [
    // 'This is a very, very, very long but incredibly smooth first TodoList',
    // 'This is a very, very, very long but incredibly smooth second TodoList',
    // 'This is an incredibly long but smooth third TodoList',
    // 'This is very, very, very long and incredibly long but it's a neat fourth TodoList',]}// Finally export the data defaultState as state
export default (state = defaultState, action) => {
  // 5. Print state and action
  console.log(state);
  console.log(action);

  // 6. Obtain data from reducer. Js and return to process the result
  if(action.type === 'change_input_value') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState
  }
  
  return state;
}
Copy the code

At this point, we open the Console, enter the content in the Input box, and look at the Console output:

Now let’s analyze what we did when we changed the code:

  1. In the Input component, we call itonChangeWhen bindinghandleInputChangeEvents.
  2. definehandleInputChangeMethods.
  3. writehandleInputChangeMethods.
  4. We are inhandleInputChangeIn the writingactionThrough thedispatchactionFrom todolist.js to reducer.js in Redux.
  5. Print it in reducer.jsstateaction.
  6. Redux received in reducer.jsstateactionAnd then we’ll have a newnewStateGo back (first to SRC /store/index.js, then SRC/todolist.js) and expect todolist.js to receive the feedback.
  7. In the TodoListconstructorThrough thestore.subscribeBinding processes the data returned by ReduxhandleStoreChange.
  8. inhandleStoreChangeIn, we directlysetStateThe state returned by Redux, i.estore.getState().

At this point, we will look at the Redux knowledge in Section 6.2 and find that the knowledge is smooth.

  • 6.2 Redux

Reference: counter

Details of the code
import { createStore } from 'redux';

/** * This is a reducer pure function of the form (state, action) => state. * describes how an action converts state to the next state. The form of * * state is up to you. It can be primitive types, arrays, objects, *, or even immutable.js generated data structures. The only important point is that * you need to return the brand new object when state changes, not modify the parameters passed in. * * The following example uses the 'switch' statement and strings to do this, but you can write helper classes * depending on different conventions (such as method mappings) that apply to your project. * /
function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1;
  case 'DECREMENT':
    return state - 1;
  default:
    returnstate; }}// Create a Redux store to store the state of the application.
The API is {subscribe, dispatch, getState}.
let store = createStore(counter);

// Updates can be subscribed manually, or events can be bound to the view layer.
store.subscribe((a)= >
  console.log(store.getState())
);

// The only way to change internal state is to dispatch an action.
// Actions can be serialized, logged and stored, and later executed in playback mode
store.dispatch({ type: 'INCREMENT' });
/ / 1
store.dispatch({ type: 'INCREMENT' });
/ / 2
store.dispatch({ type: 'DECREMENT' });
/ / 1
Copy the code

7.2 Button submits data

Returns the directory

Below, we provide a carriage return event for Input, as well as a Button submission event. You can refer to the Input event and write it yourself first, and then check this chapter for more harvest.

src/TodoList.js

Details of the code
import React, { Component } from 'react'; // Introduce React and its Component
import './index.css'; / / the introduction of the index. The CSS
import { Input, Button, List } from 'antd'; // Import antD components
import 'antd/dist/antd.css'; // Introduce the antD style
import store from './store'; // Import store, you can understand to provide data for store. ./store is short for./store/index.js

class TodoList extends Component {

  // Get data from constructor using the store.getState() method and assign the value to state
  constructor(props) {
    super(props);
    // We try to print store.getState() in Console
    // console.log(store.getState());
    this.state = store.getState();
    
    // Handle the handleInputChange method
    this.handleInputChange = this.handleInputChange.bind(this);

    // Bind the method handleStoreChange to handle the data returned by Redux
    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);

    // 2. Handle handleAddItem
    this.handleAddItem = this.handleAddItem.bind(this);

    // 7. Handle handleInputKeyUp
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
  }

  render() {
    return<div className="todo"> <h1>TodoList</h1> </div> {/* Use Input, Button component */} <div ClassName ="todo-action"> {/* Input bound handleInputChange event */} {/* 6. Input bound return event: handleInputKeyUp */} <Input placeholder='todo' className="todo-input" value={this.state.inputValue} onChange={this.handleInputChange} onKeyUp={this.handleInputKeyUp} /> {/* 1. <Button type="primary" className="todo-submit" onClick={this.handleAddItem} > Submit </Button> </div> {/* Use the List component */} {/* replace the original data with state todoList */} <div className="todo-list"> <List size="large" bordered dataSource={this.state.todoList} renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)} /> </div> </div> ); } // Write handleInputChange handleInputChange(e) {// Pass data to store const action = {type: 'change_input_value', value: e.target.value } store.dispatch(action); } // Handle data in handleStoreChange handleStoreChange() {this.setState(store.getState()); } handleAddItem() {// 4. Const action = {type: 'add_todo_item'} store.dispatch(action); } // 8. Bind handleAddItem handleInputKeyUp(e) {if(e.keycode === 13) { this.handleAddItem(); } } } export default TodoList;Copy the code

src/store/reducer.js

Details of the code
// Define a data defaultState
const defaultState = {
  inputValue: ' '.todoList: [
    // 'This is a very, very, very long but incredibly smooth first TodoList',
    // 'This is a very, very, very long but incredibly smooth second TodoList',
    // 'This is an incredibly long but smooth third TodoList',
    // 'This is very, very, very long and incredibly long but it's a neat fourth TodoList',]}// Finally export the data defaultState as state
export default (state = defaultState, action) => {
  // Prints state and action
  // console.log(state);
  // console.log(action);

  // Obtain data from reducer. Js and return to process the result
  if(action.type === 'change_input_value') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }

  // 5. Obtain data from reducer. Js and return to process the result
  if(action.type === 'add_todo_item') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.push(newState.inputValue);
    newState.inputValue = ' ';
    return newState;
  }

  return state;
}
Copy the code

At this point, our Button submission events are completed, at this point the page function implementation:

OK, let’s comb through the process again:

  1. Define click execution for ButtonhandleAddItemmethods
  2. To deal withhandleAddItemmethods
  3. writehandleAddItemmethods
  4. throughdispatch(action)Pass the data tostore
  5. Obtain data from reducer.js and return to process the results
  6. Input Binds the carriage return event:handleInputKeyUp
  7. To deal withhandleInputKeyUpmethods
  8. KeyUp method for InputhandleInputKeyUpThe bindinghandleAddItem

Note that we did handleStoreChange in the Input, so we did not write store.subscribe() to monitor data changes, so pay attention to the whole process.

7.3 Deleting the TodoItem List Item

Returns the directory

So next, let’s click add remove event for the list item.

src/TodoList.js

Details of the code
import React, { Component } from 'react'; // Introduce React and its Component
import './index.css'; / / the introduction of the index. The CSS
import { Input, Button, List } from 'antd'; // Import antD components
import 'antd/dist/antd.css'; // Introduce the antD style
import store from './store'; // Import store, you can understand to provide data for store. ./store is short for./store/index.js

class TodoList extends Component {

  // Get data from constructor using the store.getState() method and assign the value to state
  constructor(props) {
    super(props);
    // We try to print store.getState() in Console
    // console.log(store.getState());
    this.state = store.getState();
    
    // Handle the handleInputChange method
    this.handleInputChange = this.handleInputChange.bind(this);

    // Bind the method handleStoreChange to handle the data returned by Redux
    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);

    // Handle the handleAddItem method
    this.handleAddItem = this.handleAddItem.bind(this);

    // Handle the handleInputKeyUp method
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
  }

  render() {
    return<div className="todo"> <h1>TodoList</h1> </div> {/* Use Input, Button component */} <div ClassName ="todo-action"> {/* Input bind handleInputChange event */} {/* Input bind return event: handleInputKeyUp */} <Input placeholder='todo' className="todo-input" value={this.state.inputValue} OnChange ={this.handleInputChange} onKeyUp={this.handleInputKeyUp} /> {/* Define the handleAddItem method */} <Button Type ="primary" className="todo-submit" onClick={this.handleAddItem} > submit </Button> </div> {/* Use the List component */} {/* use the original TodoList */} {/* 1. List click events bind handleDeleteItem method */} <div className="todo-list"> < list size="large" bordered dataSource={this.state.todoList} renderItem={(item, index) => ( <List.Item onClick={this.handleDeleteItem.bind(this, index)}>{index + 1} - {item}</List.Item> )} /> </div> </div> ); } // Write handleInputChange handleInputChange(e) {// Pass data to store const action = {type: 'change_input_value', value: e.target.value } store.dispatch(action); } // Handle data in handleStoreChange handleStoreChange() {this.setState(store.getState()); } // Write the handleAddItem method handleAddItem() {// Pass data to store const action = {type: 'add_todo_item' } store.dispatch(action); } // Bind handleAddItem handleInputKeyUp(e) {if(e.keycode === 13) { this.handleAddItem(); }} // 2. HandleDeleteItem (index) {console.log(index); Const action = {type: 'delete_todo_item', index} store.dispatch(action); } } export default TodoList;Copy the code

src/store/reducer.js

Details of the code
// Define a data defaultState
const defaultState = {
  inputValue: ' '.todoList: [
    // 'This is a very, very, very long but incredibly smooth first TodoList',
    // 'This is a very, very, very long but incredibly smooth second TodoList',
    // 'This is an incredibly long but smooth third TodoList',
    // 'This is very, very, very long and incredibly long but it's a neat fourth TodoList',]}// Finally export the data defaultState as state
export default (state = defaultState, action) => {
  // Prints state and action
  // console.log(state);
  // console.log(action);

  // Obtain data from reducer. Js and return to process the result
  if(action.type === 'change_input_value') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }

  // Obtain data from reducer. Js and return to process the result
  if(action.type === 'add_todo_item') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.push(newState.inputValue);
    newState.inputValue = ' ';
    return newState;
  }

  // 4. Obtain data from reducer. Js and return to process the result
  if(action.type === 'delete_todo_item') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.splice(action.index, 1);
    return newState;
  }

  return state;
}
Copy the code

Now let’s start with a functional demonstration:

Let’s take a look at our programming ideas:

  1. List click events bind the handleDeleteItem method. At this point, we need to bindthisAnd pass the valueindex, that is, two values, so we directly in the code:this.handleDeleteItem.bind(this, index)
  2. Write the handleDeleteItem method
  3. Data is passed to the store via Dispatch (Action)
  4. Obtain data from reducer.js and return to process the results

This completes the deletion of the list item.

At this point, we are familiar with the methods for retrieving and modifying data in Reudx.

Optimization: Extract type from action

Returns the directory

In the previous section, we finished building TodoList, so we’re done.

But, you know, this article is called React Demo Two-Todolist.

That said, not only do we have to upgrade to Redux, but we have to upgrade further, paving the way for the development of larger projects.

Therefore, this chapter starts to optimize the processing.

In the above code, do we notice that the type of our action is written to todolist.js?

  • change_input_value
  • add_todo_item
  • delete_todo_item

So we need to handle type, and we’ll add an actiontypes.js in the store directory:

src/store/actionTypes.js

Details of the code
1. Define actionTypes
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

Then use it in todolist.js and reducer.js:

src/TodoList.js

Details of the code
import React, { Component } from 'react'; // Introduce React and its Component
import './index.css'; / / the introduction of the index. The CSS
import { Input, Button, List } from 'antd'; // Import antD components
import 'antd/dist/antd.css'; // Introduce the antD style
import store from './store'; // Import store, you can understand to provide data for store. ./store is short for./store/index.js
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'; // 2. Reference actionTypes

class TodoList extends Component {

  // Get data from constructor using the store.getState() method and assign the value to state
  constructor(props) {
    super(props);
    // We try to print store.getState() in Console
    // console.log(store.getState());
    this.state = store.getState();
    
    // Handle the handleInputChange method
    this.handleInputChange = this.handleInputChange.bind(this);

    // Bind the method handleStoreChange to handle the data returned by Redux
    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);

    // Handle the handleAddItem method
    this.handleAddItem = this.handleAddItem.bind(this);

    // Handle the handleInputKeyUp method
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
  }

  render() {
    return<div className="todo"> <h1>TodoList</h1> </div> {/* Use Input, Button component */} <div ClassName ="todo-action"> {/* Input bind handleInputChange event */} {/* Input bind return event: handleInputKeyUp */} <Input placeholder='todo' className="todo-input" value={this.state.inputValue} OnChange ={this.handleInputChange} onKeyUp={this.handleInputKeyUp} /> {/* Define the handleAddItem method */} <Button Type ="primary" className="todo-submit" onClick={this.handleAddItem} > submit </Button> </div> {/* Use the List component */} {/* use the original <div className="todo-list"> < list size="large" bordered dataSource={this.state.todoList} renderItem={(item, index) => ( <List.Item onClick={this.handleDeleteItem.bind(this, index)}>{index + 1} - {item}</List.Item> )} /> </div> </div> ); } // Write handleInputChange handleInputChange(e) {// Pass data to store via dispatch(action). ActionTypes const action = {type: CHANGE_INPUT_VALUE, value: e.target.value} store.dispatch(action); } // Handle data in handleStoreChange handleStoreChange() {this.setState(store.getState()); } // Write the handleAddItem method handleAddItem() {// Pass the data to store via dispatch(action). ActionTypes const action = {type: ADD_TODO_ITEM} store.dispatch(action); } // Bind handleAddItem handleInputKeyUp(e) {if(e.keycode === 13) { this.handleAddItem(); }} handleDeleteItem(index) {console.log(index); // Send data to store via dispatch(action). ActionTypes const action = {type: DELETE_TODO_ITEM, index} store.dispatch(action); } } export default TodoList;Copy the code

src/store/reducer.js

Details of the code
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'; // 2. Reference actionTypes

// Define a data defaultState
const defaultState = {
  inputValue: ' '.todoList: [
    // 'This is a very, very, very long but incredibly smooth first TodoList',
    // 'This is a very, very, very long but incredibly smooth second TodoList',
    // 'This is an incredibly long but smooth third TodoList',
    // 'This is very, very, very long and incredibly long but it's a neat fourth TodoList',]}// Finally export the data defaultState as state
export default (state = defaultState, action) => {
  // Prints state and action
  // console.log(state);
  // console.log(action);

  // Obtain data from reducer. Js and return to process the result
  // 3. Use actionTypes
  if(action.type === CHANGE_INPUT_VALUE) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }

  // Obtain data from reducer. Js and return to process the result
  // 3. Use actionTypes
  if(action.type === ADD_TODO_ITEM) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.push(newState.inputValue);
    newState.inputValue = ' ';
    return newState;
  }

  // Obtain data from reducer. Js and return to process the result
  // 3. Use actionTypes
  if(action.type === DELETE_TODO_ITEM) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.splice(action.index, 1);
    return newState;
  }

  return state;
}
Copy the code

In addition, the point of extracting actiontypes.js is to fix the action.type value so that you don’t get an error if you use it in two different places.

Optimization: Extract the entire action

Returns the directory

As the code volume increases, we find that the comments gradually increase, so here, we first remove all the comments, please familiarize yourself with the code flow of the above chapter.

After the cleanup, we can see that although the actionType is extracted, our action is still very complicated to manage when the page is complex enough, so we try to extract the entire action.

Let’s create a new actionactionine.js in the Store directory:

src/store/actionCreators.js

Details of the code
// 1. Introduce actionTypes
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'

// 2. Export corresponding actions
export const getInputChangeAction = (value) = > ({
  type: CHANGE_INPUT_VALUE,
  value
})

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

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

Todolist.js = todolist.js = todolist.js = todolist.js

src/TodoList.js

Details of the code
import React, { Component } from 'react';
import './index.css';
import { Input, Button, List } from 'antd';
import 'antd/dist/antd.css';
import store from './store';
import { getChangeInputValue, getAddTodoItem, getDeleteTodoItem } from './store/actionCreators'; // 3. Introduce actionCreators

class TodoList extends Component {

  constructor(props) {
    super(props);
    this.state = store.getState();
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);
    this.handleAddItem = this.handleAddItem.bind(this);
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
  }

  render() {
    return( <div className="todo"> <div className="todo-title"> <h1>TodoList</h1> </div> <div className="todo-action"> <Input placeholder='todo' className="todo-input" value={this.state.inputValue} onChange={this.handleInputChange} /> <Button type="primary" className="todo-submit" onClick={this.handleaddItem} > Submit </Button> </div> <div className="todo-list"> <List size="large" bordered dataSource={this.state.todoList} renderItem={(item, index) => ( <List.Item onClick={this.handleDeleteItem.bind(this, index)}>{index + 1} - {item}</List.Item> )} /> </div> </div> ); } handleInputChange(e) { // 4. Use the actionCreators variable getChangeInputValue const action = getChangeInputValue(e.target.value); store.dispatch(action); } handleStoreChange() { this.setState(store.getState()); } handleAddItem() {// 4. Use the actionCreators app getAddTodoItem const action = getAddTodoItem(); store.dispatch(action); } handleInputKeyUp(e) { if(e.keyCode === 13) { this.handleAddItem(); } } handleDeleteItem(index) { // 4. Use the actionCreators option getAddTodoItem const Action = getDeleteTodoItem(index); store.dispatch(action); } } export default TodoList;Copy the code

In this way, we can extract the entire action, which is very convenient for our work on large projects.

Optimization: UI components and container components

Returns the directory

Now, throw out two definitions:

  • UI components – dumb components that do page rendering
  • Container components – Smart components that do the logic of the page

Let’s break up the code without explaining why we have these two definitions.

Here, we split the components:

src/TodoList.js

Details of the code
import React, { Component } from 'react';
import './index.css';
import 'antd/dist/antd.css';
import store from './store';
import { getChangeInputValue, getAddTodoItem, getDeleteTodoItem } from './store/actionCreators';
// 1. Import antD components such as Input to TodoListUI, and import TodoListUI
import TodoListUI from './TodoListUI';

class TodoList extends Component {

  constructor(props) {
    super(props);
    this.state = store.getState();
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleAddItem = this.handleAddItem.bind(this);
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
    this.handleDeleteItem = this.handleDeleteItem.bind(this);

    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);
  }

  render() {
    return (
      // 2. Write TodoListUI and pass parameters to TodoListUI
      <TodoListUI
        inputValue={this.state.inputValue}
        todoList={this.state.todoList}
        handleInputChange={this.handleInputChange}
        handleInputKeyUp={this.handleInputKeyUp}
        handleAddItem={this.handleAddItem}
        handleDeleteItem={this.handleDeleteItem}
      />
    );
  }

  handleInputChange(e) {
    // Fix Antd bug
    e.persist();
    const action = getChangeInputValue(e.target.value);
    store.dispatch(action);
  }

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

  handleAddItem() {
    const action = getAddTodoItem();
    store.dispatch(action);
  }

  handleInputKeyUp(e) {
    // Fix Antd bug
    e.persist();
    if(e.keyCode === 13) {
      this.handleAddItem();
    }
  }

  handleDeleteItem(index) {
    // Fix Antd bug
    index.persist();
    constaction = getDeleteTodoItem(index); store.dispatch(action); }}export default TodoList;
Copy the code

Here, we extract the content of render into a subcomponent called TodoListUI in the SRC directory. We use Todolist.js as a container component and just pass the data to TodoListUI. Then we write the UI component:

src/TodoListUI.js

Details of the code
Import React, {Component} from 'React '; import { Input, Button, List } from 'antd'; class TodoListUI extends Component { render() { return ( // 4. Receive the data passed in todolist.js <div className="todo"> <div className="todo-title"> <h1>TodoList</h1> </div> <div className="todo-action"> <Input placeholder='todo' className="todo-input" value={this.props.inputValue} onChange={this.props.handleInputChange} onKeyUp={this.props.handleInputKeyUp} /> <Button type="primary" </Button> </div> <div className="todo-list"> {/* 5. When you're dealing with handleDeleteItem, */} <List size="large" bordered dataSource={this.props. TodoList} renderItem={(item, index) => ( <List.Item onClick={() => {this.props.handleDeleteItem(index)}}> {index + 1} - {item} </List.Item> )} /> </div> </div> ); } } export default TodoListUI;Copy the code

This completes the separation of the UI component from the container component.

What we do is:

  1. Migrate antD component imports such as Input to TodoListUI and import TodoListUI
  2. Write TodoListUI and pass parameters to TodoListUI
  3. Introduce components such as Input
  4. Receives the data passed in todolist.js
  5. When you’re dealing with the handleDeleteItem, remember that the index value needs to be processed again

In this way, we are done extracting pages, and when we have too many pages, we separate the content into the UI component. Container components, on the other hand, can contain an infinite number of UI components. So:

A container component is a smart component that controls the whole. UI components are dumb components that simply execute the events passed by the container component and render the page.

Optimization: stateless components

Returns the directory

A component is called stateless when all it does is the render() function.

In the case of TodoList, our TodoListUI only does render(), so we can use TodoListUI as a stateless component:

src/TodoListUI

Details of the code
Import react from 'react'; import { Input, Button, List } from 'antd'; // class TodoListUI extends Component { // 2. Const TodoListUI = (props) => {// 3. Render (// 4. Receive the data passed in todolist.js <div className="todo"> <div className="todo-title"> <h1>TodoList</h1> </div> <div className="todo-action"> {/* 5. This. Props */} <Input placeholder='todo' className="todo-input" value={props. onChange={props.handleInputChange} onKeyUp={props.handleInputKeyUp} /> <Button type="primary" className="todo-submit" OnClick ={props. HandleAddItem} > Submit </Button> </div> <div className="todo-list"> < list size="large" bordered dataSource={props.todoList} renderItem={(item, index) => ( <List.Item onClick={() => {props.handleDeleteItem(index)}}> {index + 1} - {item} </List.Item> )} /> </div> </div> ); } export default TodoListUI;Copy the code

Here, five tasks have been done:

  1. We don’t need reactComponentThat’s why we got rid of itComponent
  2. Perform the stateless component definition, and then obtain the data passed by the parent component through props
  3. We don’t need to render, just return
  4. Receives the data passed in todolist.js
  5. Let’s change this. Props to props

Twelve-ending: Call Axios, redux-base completes

Returns the directory

Finally comes the final step, we need to obtain the interface provided by the back end, for further development.

  • The introduction of Axios:cnpm i axios -S
  • incomponentDidMountTo obtain interface data, and go through the process, and finally render to the page:

TodoList.js

Details of the code
import React, { Component } from 'react';
import './index.css';
import 'antd/dist/antd.css';
import store from './store';
// 7. Import initListAction from actionCreators
import { getChangeInputValue, getAddTodoItem, getDeleteTodoItem, initListAction } from './store/actionCreators';
import TodoListUI from './TodoListUI';
import axios from 'axios'; // 1. Introduce axios

class TodoList extends Component {

  constructor(props) {
    super(props);
    this.state = store.getState();
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleAddItem = this.handleAddItem.bind(this);
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
    this.handleDeleteItem = this.handleDeleteItem.bind(this);

    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);
  }

  render() {
    return (
      <TodoListUI
        inputValue={this.state.inputValue}
        todoList={this.state.todoList}
        handleInputChange={this.handleInputChange}
        handleInputKeyUp={this.handleInputKeyUp}
        handleAddItem={this.handleAddItem}
        handleDeleteItem={this.handleDeleteItem}
      />); ComponentDidMount () {componentDidMount() {componentDidMount() { axios.get('https://www.easy-mock.com/mock/5ca803587e5a246db3d100cb/todolist').then( (res) => { console.log(res.data.todolist); // 3. Dispatch the interface data to the action, so you need to go to actionactionine.js to create the action. Const action = initListAction(res.data.todolist); store.dispatch(action); })} handleInputChange(e) {// Fix Antd bug e.persist(); const action = getChangeInputValue(e.target.value); store.dispatch(action); } handleStoreChange() { this.setState(store.getState()); } handleAddItem() { const action = getAddTodoItem(); store.dispatch(action); } handleInputKeyUp(e) {// Fix Antd bug e.persist(); if(e.keyCode === 13) { this.handleAddItem(); }} handleDeleteItem(index) {// Fix Antd bug index.persist(); const action = getDeleteTodoItem(index); store.dispatch(action); } } export default TodoList;Copy the code

actionCreators.js

Details of the code
Import INIT_LIST_ACTION from actionTypes
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION } from './actionTypes';

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

export const getAddTodoItem = (a)= > ({
  type: ADD_TODO_ITEM
})

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

// 4. Write the exported initListAction, so you need to introduce INIT_LIST_ACTION in actionTypes first
export const initListAction = (data) = > ({
  type: INIT_LIST_ACTION,
  data
})
Copy the code

actionTypes.js

Details of the code
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 INIT_LIST_ACTION
export const INIT_LIST_ACTION = 'init_list_action';
Copy the code

reducer.js

Details of the code
Reference INIT_LIST_ACTION from actionTypes
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION } from './actionTypes';

const defaultState = {
  inputValue: ' '.todoList: []}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_TODO_ITEM) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.push(newState.inputValue);
    newState.inputValue = ' ';
    return newState;
  }

  if(action.type === DELETE_TODO_ITEM) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.splice(action.index, 1);
    return newState;
  }

  // 10. Accept the data passed by TodoList, process it and return it
  if(action.type === INIT_LIST_ACTION) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList = action.data;
    return newState;
  }

  return state;
}
Copy the code

Now we have done the call to Axios and rendered it to the page.

  1. TodoList. Js — — introductionaxios
  2. Todolist.js — done in componentDidMount()axiosInterface call
  3. Todolist.js – interface datadispatchaction“, so you need to go to actionCreatorsaction
  4. Actionactioncreators. Js — Written and exportedinitListAction, so it needs to be introduced in actionTypes firstINIT_LIST_ACTION
  5. Actionactionavails.js — imported from actionTypesINIT_LIST_ACTION
  6. ActionTypes. Js — — the exportINIT_LIST_ACTIONTo actionCreators
  7. Todolist.js — imported from actionCreatorsinitListAction
  8. TodoList. Js – createactiondispatchTo reducer. In js
  9. Reducer.js — reference from actionTypesINIT_LIST_ACTION
  10. Reducer. Js — Accept data from TodoList, process it and return it

This completes the interface call, and the page should look like this:

At this point, we are done with redux-base.

However, this is a simple use of Redux, and we can see that using Redux alone is complicated for the project, so we need redux-Thunk and Redux-Saga middleware for Redux. And try react-redux at the end.

Step 13: Redux middleware

Returns the directory

  • What is middleware?

Middleware is a plug-in arranged between who and who.

  • What is Redux middleware?

Look at the picture:

As you can see in the figure above, when we Dispatch an Action to a Store via Dispatch, we reference middleware in the Dispatch for processing. It encapsulates the Dispatch upgrade so that we can use not only objects at Dispatch, but also method functions.

This way, when we pass an object to Dispatch, it’s no different than when we normally use Redux. However, when we pass a function to Dispatch, if we use redux-thunk or redux-saga, they will handle it, allowing us to call the function as well.

So, Redux’s middleware, in a nutshell, is a packaged upgrade to Dispatch.

Level 14: Redux-Thunk middleware for Ajax request management

Returns the directory

In Chapter 12, we made Ajax requests in TodoList, which is fine.

However, with the increasing number of Ajax requests, if we were to write them all in the page, it would make the page bloated.

This is where redux-thunk comes in. Redux-thunk allows asynchronous requests and complex business logic to be extracted and processed elsewhere.

Let’s copy the redux-base code to the redux-thunk directory and execute:

Note: there is no need to copy the node_modules folder

  • Install dependencies:npm i
  • Operating Projects:npm run start

Then, we start referencing redux-thunk:

  • Redux Thunk: Github address
  • Installation:npm i redux-thunk -S
  • Tutorial examples:

test.js

Details of the code
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';

const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
)
Copy the code

It looks very easy no, so let’s try it in the project.

src/store/index.js

Details of the code
// 2. Import applyMiddleware from Redux. ApplyMiddleware is used to apply redux middleware
// 3. Introduce the compose function because we are using two middleware: redux-thunk and Redux-devTools-extension, which requires the compose assist
import { createStore, applyMiddleware, compose } from 'redux';
import reducer from './reducer';
// 1. Import thunk from redux-thunk
import thunk from 'redux-thunk';

// 3. Use redux-devTools-extension middleware
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;

// 4. Use applyMiddleware to extend this
const enhancer = composeEnhancers(
  applyMiddleware(thunk),
);

// 5. Make the enhancer call in createStore
const store = createStore(
  reducer,
  enhancer
);

export default store;
Copy the code

Here, we did a few things:

  1. fromredux-thunkThe introduction ofthunk
  2. fromreduxThe introduction ofapplyMiddleware.applyMiddlewareIs used to apply multiple Redux middleware
  3. The introduction ofcomposeFunction because we are using two middleware:redux-thunkAs well asredux-devtools-extension, you need tocomposeauxiliary
  4. useredux-devtools-extensionThe middleware
  5. useapplyMiddlewareTo extend this, i.eredux-thunkMiddleware plusredux-devtools-extensionThe middleware
  6. increateStoreforenhancercall

In this way, we use the Redux-Thunk middleware plus the Redux-DevTools-Extension middleware in one project at the same time, thus achieving the redux-Thunk reference.

Next, we use redux-thunk

src/store/actionCreators.js

Details of the code
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION } from './actionTypes';
// 1. Cut axios from todolist.js to actionActionine.js
import axios from 'axios';

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

export const getAddTodoItem = (a)= > ({
  type: ADD_TODO_ITEM
})

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

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

// 2. Move axios.get() for componentDidMount() from TodoList to actionActionine.js
// 3. Before redux-thunk, we could only use objects in actionActionviets.js, now we can also use functions.
export const getTodoList = (a)= > {
  // 7. When we use getTodoList, we can also pass store dispatches to use in the code below
  return (dispatch) = > {
    axios.get('https://www.easy-mock.com/mock/5ca803587e5a246db3d100cb/todolist').then( (res) = > {
      // 8. Use the initListAction method in actionActionine.js directly and dispatch the action
      constaction = initListAction(res.data.todolist); dispatch(action); }}})Copy the code

src/TodoList.js

Details of the code
import React, { Component } from 'react';
import './index.css';
import 'antd/dist/antd.css';
import store from './store';
// 4. Reference getTodoList in actionCrests.js in todolist.js
import { getChangeInputValue, getAddTodoItem, getDeleteTodoItem, getTodoList } 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.handleAddItem = this.handleAddItem.bind(this);
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
    this.handleDeleteItem = this.handleDeleteItem.bind(this);

    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);
  }

  render() {
    return (
      <TodoListUI
        inputValue={this.state.inputValue}
        todoList={this.state.todoList}
        handleInputChange={this.handleInputChange}
        handleInputKeyUp={this.handleInputKeyUp}
        handleAddItem={this.handleAddItem}
        handleDeleteItem={this.handleDeleteItem}
      />); } componentDidMount() {// 5. Call getTodoList in componentDidMount. If we hadn't used redux-thunk, we would have had to use objects, but now we can use functions. const action = getTodoList(); // 6. When we dispatch the action, we call getTodoList() in step 1 to get the data store.dispatch(action); } handleInputChange(e) { const action = getChangeInputValue(e.target.value); store.dispatch(action); } handleStoreChange() { this.setState(store.getState()); } handleAddItem() { const action = getAddTodoItem(); store.dispatch(action); } handleInputKeyUp(e) { if(e.keyCode === 13) { this.handleAddItem(); } } handleDeleteItem(index) { const action = getDeleteTodoItem(index); store.dispatch(action); } } export default TodoList;Copy the code

At this point, we may be at our wits’ end, so here’s the idea:

  1. theaxiosCut from todolist.js to actionActionviets.js
  2. Put the TodoList file incomponentDidMount()axios.get()Move to actionCreators. Js
  3. Without usingredux-thunkPreviously, we could only use objects in actionActioncreators. Now we can use functions as well.
  4. In todolist.js, refer to actionActionine.jsgetTodoList()And remove the ones that are not referenced againinitListAction
  5. incomponentDidMount()In the callgetTodoList(). If we don’t use itredux-thunkWe can only use objects, but now we can use functions.
  6. When wedispatchaction, we call step 1’sgetTodoList()To obtain the data
  7. When we usegetTodoList()When, we can also passstoredispatchTo be used in the following code
  8. Use actionCreators. Js directlyinitListActionMethods, anddispatchaction

In this way, we extract the Axios interface call into actionActionine.js via redux-thunk.

Why do we have to go through so many steps to extract it from todolist.js when it was working so well?

What we need to know is that when the page is complex enough, the project is big enough, and the code is getting bigger and bigger, it’s not easy to manage the interface if all our interface calls are in the container component, and finally if we need to change an interface, we have to search through the page.

With the redux-Thunk call, we extract the interface code from the container components so that the interface code logic is interface code logic and the business code logic is business code logic.

Furthermore, redux-Thunk extraction facilitates our automated testing. Of course, we don’t know what automated testing will look like, but we can take comfort in the fact that it always makes sense.

Redux-thunk redux-Thunk Redux-Thunk redux-Thunk redux-Thunk

Level 15: Redux-Saga middleware for Ajax request management

Returns the directory

With the experience of Redux-Thunk, we can also learn about Redux-Saga.

First let’s copy a file from redux-base to the redux-saga directory.

Note: there is no need to copy the node_modules folder

  • Install dependencies:npm i
  • Operating Projects:npm run start

Then we start quoting Redux-Saga:

  • Redux Saga: Github address
  • Installation:npm i redux-saga -S
  • Tutorial examples:

test.js

Details of the code
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'

import reducer from './reducers'
import mySaga from './sagas'

// create the saga middleware
const sagaMiddleware = createSagaMiddleware()
// mount it on the Store
const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
)

// then run the saga
sagaMiddleware.run(mySaga)

// render the application
Copy the code

As you can see, the redux-saga reference is as simple as Redux-thunk. However, please accept the form of complexity and continue to learn.

Redux-saga redux-saga redux-saga redux-saga redux-saga redux-saga

src/store/index.js

Details of the code
// 1. Introduce applyMiddleware and compose for multiple middleware processes
import { createStore, applyMiddleware, compose } from 'redux';
import reducer from './reducer';
// 2. Introduce redux-saga createSagaMiddleware
import createSagaMiddleware from 'redux-saga';
// 6. Create and reference sagas.js file under store
import todoSaga from './sagas';

// 3. Call createSagaMiddleware
const sagaMiddleware = createSagaMiddleware();

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

Call composeEnhancers for multi-middleware processing
const enhancer = composeEnhancers(
  applyMiddleware(sagaMiddleware),
);

const store = createStore(
  reducer,
  enhancer
);

// 7. Use todoSaga
sagaMiddleware.run(todoSaga);

export default store;
Copy the code

src/store/sagas.js

Details of the code
// 8. Define todoSaga using generator functions
function* todoSaga() {}// export the generator function
export default todoSaga;
Copy the code

This completes the redux-saga reference, which is roughly as follows:

  1. The introduction ofapplyMiddlewarecomposeProcessing of multiple middleware
  2. The introduction ofredux-sagacreateSagaMiddleware
  3. callcreateSagaMiddlewaremethods
  4. definecomposeEnhancers
  5. callcomposeEnhancersMulti-middleware processing
  6. Create and referencestoreSagas.js filetodoSaga
  7. throughsagaMiddlewareusetodoSaga
  8. usegeneratorFunction definition sagas.js file
  9. willgeneratorFunction export

At the same time, we observe the page, there is no error, indicating that we quoted correctly.

ComponentDidMount () = axios.get(); SRC /store/sagas.js;

  1. src/TodoList.js
Details of the code
import React, { Component } from 'react';
import './index.css';
import 'antd/dist/antd.css';
import store from './store';
// 1. Delete initListAction and the following axios and introduce the getInitList in actionactionine.js
import { getChangeInputValue, getAddTodoItem, getDeleteTodoItem, getInitList } 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.handleAddItem = this.handleAddItem.bind(this);
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
    this.handleDeleteItem = this.handleDeleteItem.bind(this);

    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);
  }

  render() {
    return (
      <TodoListUI
        inputValue={this.state.inputValue}
        todoList={this.state.todoList}
        handleInputChange={this.handleInputChange}
        handleInputKeyUp={this.handleInputKeyUp}
        handleAddItem={this.handleAddItem}
        handleDeleteItem={this.handleDeleteItem}
      />); } componentDidMount() {// 5. Call getInitList and dispatch action with dispatch. In this case, not only reducer. Js can receive this action, but our sagas.js can also receive this action. const action = getInitList(); store.dispatch(action); } handleInputChange(e) { const action = getChangeInputValue(e.target.value); store.dispatch(action); } handleStoreChange() { this.setState(store.getState()); } handleAddItem() { const action = getAddTodoItem(); store.dispatch(action); } handleInputKeyUp(e) { if(e.keyCode === 13) { this.handleAddItem(); } } handleDeleteItem(index) { const action = getDeleteTodoItem(index); store.dispatch(action); } } export default TodoList;Copy the code
  1. src/store/actionCreators.js
Details of the code
// 2. Import GET_INIT_LIST in actiontypes.js
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION, GET_INIT_LIST } from './actionTypes';

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

export const getAddTodoItem = (a)= > ({
  type: ADD_TODO_ITEM
})

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

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

// 3. Use GET_INIT_LIST
export const getInitList = (a)= > ({
  type: GET_INIT_LIST
});
Copy the code
  1. src/store/actionTypes.js
Details of the code
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';
// 4. Define GET_INIT_LIST and export it to actiontypes.js
export const GET_INIT_LIST = 'get_init_list';
Copy the code
  1. src/store/sagas.js
Details of the code
// 6. Reference takeEvery in redux-saga/effets
// 13. Since we do not reference store in sagas.js, we cannot use store.dispatch(), but redux-Saga provides a put method instead of the store.dispatch() method
import { takeEvery, put } from 'redux-saga/effects';
// 7. Import the GET_INIT_LIST type
import { GET_INIT_LIST } from './actionTypes';
// 11. Migrate axios import from todolist.js to sagas.js
import axios from 'axios';
// 12. Introduce initListAction in actionCreator
import { initListAction } from './actionCreators'

// 8. Use generator functions
function* todoSaga() {
  This line of code says that as soon as we receive the GET_INIT_LIST type, we execute the getInitList method
  yield takeEvery(GET_INIT_LIST, getInitList);
}

// define the getInitList method
function* getInitList() {
  try {
    Handle asynchronous functions in sagas.js
    const res = yield axios.get('https://www.easy-mock.com/mock/5ca803587e5a246db3d100cb/todolis');
    const action = initListAction(res.data.todolist);
    // 15. After the action is processed, execute the put method
    yield put(action);
  } catch (error) {
    console.log("Interface request failed, please check todolist interface."); }}export default todoSaga;
Copy the code

In this way, we extract the asynchronous request function that calls the interface into the sagas.js file, during which we do:

  1. TodoList. Js – deleteinitListActionAnd the followingaxiosAnd introduce the getInitList in actionActioncreators. Js
  2. Actionactionmouths.js — import into actiontypes.jsGET_INIT_LIST
  3. ActionTypes. Js – useGET_INIT_LIST
  4. ActionTypes. Js — — definitionGET_INIT_LISTExport to actiontypes.js for use
  5. TodoList. Js – the callgetInitListAnd the use ofdispatchactionGive it away. At this point not only reducer.js can receive thisaction, our sagas.js can also receive thisaction.
  6. referenceredux-saga/effetsIn thetakeEvery
  7. The introduction ofGET_INIT_LISTtype
  8. usegeneratorfunction
  9. throughtakeEvery, means as soon as we receiveGET_INIT_LISTType, we will executegetInitListmethods
  10. definegetInitListmethods
  11. The TodoList. JsaxiosImport migration to sagas.js
  12. Import actionCreator. JsinitListAction
  13. Since we didn’t reference it in sagas.jsstore, so it cannot be usedstore.dispatch(), butredux-sagaIt provides us withputIn place ofstore.dispatch()Method, so we refer toputMethods.
  14. Handle asynchronous functions in sagas.js
  15. Etc.actionAfter processing, executeputMethods:yield put(action)

Thus, we successfully extracted the asynchronous request interface from TodoList into sagas.js for unified management of the interface.

In SRC /store/sagas.js, we also pass a try… catch… Method to handle the interface, and when the interface does not exist or the request is abnormal, we will know that the interface is faulty.

Conclusion: At this point, we have completed the reference and use of Redux-saga. Friends can try more to get familiar with Redux-saga.

  • Reference: Generator – Liao Xuefeng

16 Advanced: React-redux

Returns the directory

In the previous chapters, we used React, Redux, and middleware that touched on Redux: Redux-Thunk and Redux-Saga.

So, this chapter looks at React-redux.

  • What is React-redux?

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

Here, since the React-Base directory is separated from the React and Redux directory, we copied the basic code of the Simplify directory into the React-Redux directory and made the TodoList transformation. To begin our react-Redux journey.

The method of transforming Simplify into TodoList can be found in Chapter 3 initializing Projects, Chapter 4 using Ant Design, and Chapter 5 using Redux.

Jsliang posts its own initialized code below:

  1. src/index.js
Details of the code
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';

ReactDOM.render(<TodoList />, document.getElementById('root'));
Copy the code
  1. src/TodoList.js
Details of the code
import React, { Component } from 'react';
import './index.css';
import { Input, Button, List } from 'antd';
import 'antd/dist/antd.css';
import store from './store';

class TodoList extends Component {

  constructor(props) {
    super(props);
    this.state = store.getState();
  }

  render() {
    return( <div className="todo"> <div className="todo-title"> <h1>TodoList</h1> </div> <div className="todo-action"> <Input Placeholder ='todo' className="todo-input" /> <Button type="primary" className="todo-submit" </Button> </div> <div className="todo-list"> <List size="large" bordered dataSource={this.state.list} renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)} /> </div> </div> ); } } export default TodoList;Copy the code
  1. src/index.css
Details of the code
.todo {
  width: 1000px;
  margin: 20px auto 0;
  padding: 30px;
  border: 1px solid #ccc;
  border-radius: 10px;
}
.todo-title {
  text-align: center;
}
.todo-action .todo-input {
  width: 200px;
}
.todo-action .todo-submit {
  margin-left: 10px;
}
.todo-list {
  margin-top: 30px;
}
Copy the code
  1. src/store/index.js
Details of the code
import { createStore } from 'redux';
import reducer from './reducer';

const store = createStore(reducer);

export default store;
Copy the code
  1. src/store/reducer.js
Details of the code
const defaultState = {
  inputValue: ' '.list: [
    'This is very, very, very long and incredibly long but it's the first TodoList that flows smoothly.'.'This is very, very, very long and incredibly long but it's a neat second TodoList.'.'This is a very, very, very long and incredibly long but it's a very smooth third TodoList'.'This is a very, very, very long and incredibly long but it's a very smooth fourth TodoList']},export default (state = defaultState, action) => {
  return state;
}
Copy the code

Now the page is displayed as the page at the end of Chapter 4:

  • React Redux: GitHub address
  • The installationreact-redux:npm i react-redux -S

It’s time for real technology!

We refer to react-redux in SRC /index.js:

src/index.js

Details of the code
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
// 1. Introduce the react-redux Provider
import { Provider } from 'react-redux';
// 3
import store from './store';

// 2. Redefine the App using Provider
const App = (
  // 4. When Provider is connected to Store, all components in the Provider can access and use content in Store
  <Provider store={store}>
    <TodoList />
  </Provider>
)

// 5. Render App directly
ReactDOM.render(App, document.getElementById('root'));
Copy the code

This can then be used in SRC/todolist.js:

src/TodoList.js

Details of the code
import React, { Component } from 'react';
import './index.css';
import { Input, Button, List } from 'antd';
import 'antd/dist/antd.css';
In TodoList, we do not need to use import store from store and define constructor to get store. Instead, we use react-redux connect to get store
import { connect } from 'react-redux';

class TodoList extends Component {
  render() {
    return( <div className="todo"> <div className="todo-title"> <h1>TodoList</h1> </div> <div className="todo-action"> {/* 10. InputValue */} <Input placeholder='todo' className="todo-input" value={this.props </Button> <div > <div className="todo-list"> {/* 12. List */} < list size="large" bordered dataSource={this.props. List} renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)} /> </div> </div> ); }} // 8. Define the mapStateToProps method to map the data in the store to the props in the component. Const mapStateToProps = (state) => {return {// 9. InputValue: state. InputValue, // 11. List list: state.list}} // 7. To export the connect method and let TodoList connect with Store, there are two rules: MapStateToProps and Export Default Connect (mapStateToProps, NULL)(TodoList);Copy the code

Now that we have found that the code still works, let’s examine what steps we did:

  1. The introduction ofreact-reduxProvider
  2. useProviderTo redefine theApp
  3. The introduction ofstore
  4. ProviderConnect thestore, thenProviderAll the components in it can be retrieved and usedstoreThe contents of the
  5. Direct renderingApp
  6. In Todolist.js, we don’t need to use itimport store from storeAnd defineconstructorTo obtainstoreBut throughreact-reduxconnectIn order to get
  7. exportconnectTodolist.js andstoreTo make a connection, there are two rules:mapStateToPropsAnd * *
  8. definemapStateToPropsThe method,storeThe data inside is mapped to the data inside the componentprops, where parametersstateisstoreThe data inside
  9. defineinputValue
  10. useinputValue
  11. definelist
  12. uselist

This completes the store reference in todolist.js via react-redux.

Let’s try changing the value of store again:

src/TodoList.js

Details of the code
import React, { Component } from 'react';
import './index.css';
import { Input, Button, List } from 'antd';
import 'antd/dist/antd.css';
import { connect } from 'react-redux';

class TodoList extends Component {
  render() {
    return( <div className="todo"> <div className="todo-title"> <h1>TodoList</h1> </div> <div className="todo-action"> {/* 3. Bind the onChange event handleInputChange to the Input, } <Input placeholder='todo' className="todo-input" value={this.props. InputValue} OnChange = {this. Props. HandleInputChange} / > < Button type = "primary" className = "todo - submit" > submit < / Button > < / div > < div className="todo-list"> <List size="large" bordered dataSource={this.props.list} renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)} /> </div> </div> ); } } const mapStateToProps = (state) => { return { inputValue: state.inputValue, list: state.list } } // 2. Todolist. js maps the store.dispatch method to the props, so we can define the method as this.props. Const mapDispatchToProps = (dispatch) => {return {// 5. HandleInputChange (e) {const action = {type: 'change_input_value', value: e.target.value } // 6. Send actions to reducer. Js dispatch(action); }} // 1. Export default connect(mapStateToProps, mapDispatchToProps)(TodoList);Copy the code

SRC /reducer.js:

src/reducer.js

Details of the code
const defaultState = {
  inputValue: ' '.list: [
    'This is very, very, very long and incredibly long but it's the first TodoList that flows smoothly.'.'This is very, very, very long and incredibly long but it's a neat second TodoList.'.'This is a very, very, very long and incredibly long but it's a very smooth third TodoList'.'This is a very, very, very long and incredibly long but it's a very smooth fourth TodoList']},export default (state = defaultState, action) => {
  // 7. Make a deep copy of the action. Value value and return newState
  if(action.type === 'change_input_value') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }

  return state;
}
Copy the code

At this point, we did 7 steps:

  1. Used in todolist.jsmapDispatchToPropsmethods
  2. definemapDispatchToPropsMethod that todolist.js willstore.dispatchMethod mapping topropsGo, so we can get throughthis.propsTo define methods
  3. toInputThe bindingonChangeThe eventhandleInputChangeAt this point we passthis.propsTo bind methods
  4. inmapDispatchToPropsIn we passeddispatch, so it can be usedstore.dispatchmethods
  5. definehandleInputChangemethods
  6. willactionDistributing to the reducer. Js
  7. Judgment passed onaction.typeWhich one? Deep copy. Getaction.valueAnd returns the value ofnewState

Use the react-Redux function to delete items from TodoList’s list by clicking the Button to submit the Item. And download the jsliang code for reference:

  • React series article code address

Seventeen summary

Returns the directory

Now that we’re done with all the knowledge, code, and explanation, it’s time to relax and talk about what we learned in this article:

  1. Use of Ant Design
  2. Introduction and use of Redux
  3. UI components, container components, stateless components, and code extraction and encapsulation for large projects
  4. Use Axios in React
  5. To facilitate the management of the Axios interface code, we used redux-Thunk and Redux-Thunk middleware from Redux
  6. Use react-redux to walk through Redux and learn how to use react-redux

With that said, we have successfully concluded this article and are now moving on to the next update of React.

If you feel that Jsliang is written well, remember to give a “like” or “star” to the jsliang document library. Your “like” or “star” is my full power.


Jsliang advertising push: maybe partners want to know about the cloud server or partners want to buy a cloud server or partners need to renew the cloud server welcome to click on the cloud server promotion view!



Jsliang document library 由 Liang JunrongusingCreative Commons Attribution – Non-commercial Use – Same way Share 4.0 International LicenseGrant permission.

Based on theGithub.com/LiangJunron…On the creation of works.

Use rights other than those authorized by this License agreement may be obtained fromCreativecommons.org/licenses/by…Obtained.