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-base
redux-thunk
Middleware configuration - Redux-saga — Build on redux-Base
redux-saga
Middleware configuration - React-redux — perform on TodoList
react-redux
reinventing
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:
- 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
- src/
App.jsTodoList.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
- 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:
- 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
- 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:
- Introduce antD Input, Button, List components
- Introduce the antD style
- Define the data
- Use Input and Button components
- 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:
- 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.
- 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:
- 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
- Installation: Scientific Internet to find the corresponding Chrome plug-in, or Baidu download a, or through
npm install --save-dev redux-devtools
Install its developer tools. - Use:
- 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
- Go to index.js to install the code.
- 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:
createStore
: create the storestore.dispatch
Distribute the action:store.getState
: Gets all data content from the storestore.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
- 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
- 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:
- In the Input component, we call it
onChange
When bindinghandleInputChange
Events. - define
handleInputChange
Methods. - write
handleInputChange
Methods. - We are in
handleInputChange
In the writingaction
Through thedispatch
将action
From todolist.js to reducer.js in Redux. - Print it in reducer.js
state
和action
. - Redux received in reducer.js
state
和action
And then we’ll have a newnewState
Go back (first to SRC /store/index.js, then SRC/todolist.js) and expect todolist.js to receive the feedback. - In the TodoList
constructor
Through thestore.subscribe
Binding processes the data returned by ReduxhandleStoreChange
. - in
handleStoreChange
In, we directlysetState
The 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:
- Define click execution for Button
handleAddItem
methods - To deal with
handleAddItem
methods - write
handleAddItem
methods - through
dispatch(action)
Pass the data tostore
- Obtain data from reducer.js and return to process the results
- Input Binds the carriage return event:
handleInputKeyUp
- To deal with
handleInputKeyUp
methods - KeyUp method for Input
handleInputKeyUp
The 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:
- List click events bind the handleDeleteItem method. At this point, we need to bind
this
And pass the valueindex
, that is, two values, so we directly in the code:this.handleDeleteItem.bind(this, index)
- Write the handleDeleteItem method
- Data is passed to the store via Dispatch (Action)
- 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:
- Migrate antD component imports such as Input to TodoListUI and import TodoListUI
- Write TodoListUI and pass parameters to TodoListUI
- Introduce components such as Input
- Receives the data passed in todolist.js
- 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:
- We don’t need react
Component
That’s why we got rid of itComponent
- Perform the stateless component definition, and then obtain the data passed by the parent component through props
- We don’t need to render, just return
- Receives the data passed in todolist.js
- 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
- in
componentDidMount
To 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.
- TodoList. Js — — introduction
axios
- Todolist.js — done in componentDidMount()
axios
Interface call - Todolist.js – interface data
dispatch
到action
“, so you need to go to actionCreatorsaction
- Actionactioncreators. Js — Written and exported
initListAction
, so it needs to be introduced in actionTypes firstINIT_LIST_ACTION
- Actionactionavails.js — imported from actionTypes
INIT_LIST_ACTION
- ActionTypes. Js — — the export
INIT_LIST_ACTION
To actionCreators - Todolist.js — imported from actionCreators
initListAction
- TodoList. Js – create
action
并dispatch
To reducer. In js - Reducer.js — reference from actionTypes
INIT_LIST_ACTION
- 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:
- from
redux-thunk
The introduction ofthunk
- from
redux
The introduction ofapplyMiddleware
.applyMiddleware
Is used to apply multiple Redux middleware - The introduction of
compose
Function because we are using two middleware:redux-thunk
As well asredux-devtools-extension
, you need tocompose
auxiliary - use
redux-devtools-extension
The middleware - use
applyMiddleware
To extend this, i.eredux-thunk
Middleware plusredux-devtools-extension
The middleware - in
createStore
forenhancer
call
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:
- the
axios
Cut from todolist.js to actionActionviets.js - Put the TodoList file in
componentDidMount()
的axios.get()
Move to actionCreators. Js - Without using
redux-thunk
Previously, we could only use objects in actionActioncreators. Now we can use functions as well. - In todolist.js, refer to actionActionine.js
getTodoList()
And remove the ones that are not referenced againinitListAction
- in
componentDidMount()
In the callgetTodoList()
. If we don’t use itredux-thunk
We can only use objects, but now we can use functions. - When we
dispatch
了action
, we call step 1’sgetTodoList()
To obtain the data - When we use
getTodoList()
When, we can also passstore
的dispatch
To be used in the following code - Use actionCreators. Js directly
initListAction
Methods, anddispatch
该action
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:
- The introduction of
applyMiddleware
和compose
Processing of multiple middleware - The introduction of
redux-saga
的createSagaMiddleware
- call
createSagaMiddleware
methods - define
composeEnhancers
- call
composeEnhancers
Multi-middleware processing - Create and reference
store
Sagas.js filetodoSaga
- through
sagaMiddleware
usetodoSaga
- use
generator
Function definition sagas.js file - will
generator
Function 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;
- 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
- 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
- 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
- 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:
- TodoList. Js – delete
initListAction
And the followingaxios
And introduce the getInitList in actionActioncreators. Js - Actionactionmouths.js — import into actiontypes.js
GET_INIT_LIST
- ActionTypes. Js – use
GET_INIT_LIST
- ActionTypes. Js — — definition
GET_INIT_LIST
Export to actiontypes.js for use - TodoList. Js – the call
getInitList
And the use ofdispatch
将action
Give it away. At this point not only reducer.js can receive thisaction
, our sagas.js can also receive thisaction
. - reference
redux-saga/effets
In thetakeEvery
- The introduction of
GET_INIT_LIST
type - use
generator
function - through
takeEvery
, means as soon as we receiveGET_INIT_LIST
Type, we will executegetInitList
methods - define
getInitList
methods - The TodoList. Js
axios
Import migration to sagas.js - Import actionCreator. Js
initListAction
- Since we didn’t reference it in sagas.js
store
, so it cannot be usedstore.dispatch()
, butredux-saga
It provides us withput
In place ofstore.dispatch()
Method, so we refer toput
Methods. - Handle asynchronous functions in sagas.js
- Etc.
action
After processing, executeput
Methods: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:
- 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
- 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
- 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
- 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
- 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 installation
react-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:
- The introduction of
react-redux
的Provider
- use
Provider
To redefine theApp
- The introduction of
store
Provider
Connect thestore
, thenProvider
All the components in it can be retrieved and usedstore
The contents of the- Direct rendering
App
- In Todolist.js, we don’t need to use it
import store from store
And defineconstructor
To obtainstore
But throughreact-redux
的connect
In order to get - export
connect
Todolist.js andstore
To make a connection, there are two rules:mapStateToProps
And * * - define
mapStateToProps
The method,store
The data inside is mapped to the data inside the componentprops
, where parametersstate
isstore
The data inside - define
inputValue
- use
inputValue
- define
list
- use
list
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:
- Used in todolist.js
mapDispatchToProps
methods - define
mapDispatchToProps
Method that todolist.js willstore.dispatch
Method mapping toprops
Go, so we can get throughthis.props
To define methods - to
Input
The bindingonChange
The eventhandleInputChange
At this point we passthis.props
To bind methods - in
mapDispatchToProps
In we passeddispatch
, so it can be usedstore.dispatch
methods - define
handleInputChange
methods - will
action
Distributing to the reducer. Js - Judgment passed on
action.type
Which one? Deep copy. Getaction.value
And 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:
- Use of Ant Design
- Introduction and use of Redux
- UI components, container components, stateless components, and code extraction and encapsulation for large projects
- Use Axios in React
- To facilitate the management of the Axios interface code, we used redux-Thunk and Redux-Thunk middleware from Redux
- 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.