This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.
Preface ๐
React is known to be a one-way data flow, so data transfer is relatively simple and the relationship between parents and children is relatively clear. However, if we want to do more complex data transfer, react alone is not enough. Therefore, we need to use Redux for more complex data transfer.
The following article will explain the redux workflow from beginner to advanced.
Ding! Start your Redux journey ~๐
โ๏ธ 1. Basic knowledge
1. Brief introduction of Redux concept
React is a lightweight framework that doesn’t have a view layer. If you want to use it to pass data around, you pass it from parent to child, and then slowly move up the hierarchy.
But with Redux, if we want the data for a component, the data for that component will be stored in a store and managed by Redux. Then, through the Store, the data is passed step by step to the following components.
It is worth noting that we can view Redux as a combination of Reducer and Flux.
2. Redux workflow
Redux is essentially a data layer framework that puts all data in a Store. Let’s start with a picture:
You can see the store in the middle, where all the data is stored. If you look at the arrow down store, then each component is going to fetch data from the store.
We use an example to tease out the whole picture, as follows:
- โ There is one on the whole picture
store
It holds all the data, i.eA common area for storing data; - โก Each component should be from
store
Take the data inside; - โข Suppose we have a situation where we need to borrow books from a library. So we can take
react Component
After that, the borrower has to go to the librarian to get the book. And the transfer of data in the process of borrowing a book can be regarded asAction Creators
Can be understood as“What book do you want to borrow?”This sentence. - (4)
Action Creatures
Go to thestore
. At this point we’re going tostore
As alibrarianHowever, there is no way for a librarian to remember the data status of all the books. Generally speaking, it requires a record book, you want to borrow what kind of books, so she will first check; Or if you want to return a book, she also checks to see where it needs to be put back. - โค At this time, it is necessary to follow
reducers
Go to the correspondence. We can getreducers
Regard as abookLibrarians use thisbookTo record the data you need. The administratorstore
throughreducer
I know you should give it to the borrowerComponents
What kind of data.
๐ 2, using Antd TodoList page layout
1. Use Antd in your project
Open the antDesign website ๐ antD website portal to introduce it in the project first. The specific steps are as follows:
First, install ANTD. The command is as follows:
npm install antd --save
Copy the code
The second step is to introduce styles. The code is as follows:
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
Copy the code
2. Use Antd to achieve the basic layout of TodoList
First, we create a new file in the project’s SRC folder named todolist.js. The specific code is as follows:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
const data = [
'Racing car sprays burning fuel into crowd.'.'Japanese princess to wed commoner.'.'Australian walks 100km after outback crash.'.'Man charged over missing wedding girl.'.'Los Angeles battles huge wildfires.',];class TodoList extends Component {
render() {
return (
<div style={{marginTop: '10px', marginLeft: '10px'}} >
<div>
<Input placeholder="todo info" style={{ width: '300px' }} />
<Button type="primary">submit</Button>
</div>
<List
bordered
dataSource={data}
renderItem={item= > <List.Item>{item}</List.Item>} / ></div>)}}export default TodoList;
Copy the code
The browser displays:
Create a store in redux
(1) Create a store
Next we create a store in the project. Start by creating a new folder under the project’s SRC folder called Store. Next, under the Store folder, we create a new file named index.js. The specific code is as follows:
import { createStore } from "redux";
import reducer from './reducer';
const store = createStore(reducer);
export default store;
Copy the code
Next, go ahead and create a new file under the Store folder called Reducer.js. The specific code is as follows:
const defaultStore = {
inputValue: ' '.list: []};export default (state = defaultStore, action) => {
return state;
}
Copy the code
This completes the creation of the Store in the project.
(2) Use store in the project
After creating the Store, let’s use it in the project initially to see how it works. Add the data in store first, and transform the reducer. Js file in store. The specific code is as follows:
const defaultStore = {
inputValue: '123'.list: [1.2]};export default (state = defaultStore, action) => {
return state;
}
Copy the code
Todolist.js was then modified. The specific code is as follows:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store';
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState()
}
render() {
return (
<div style={{marginTop: '10px', marginLeft: '10px'}} >
<div>
<Input placeholder={this.state.inputValue} style={{ width: '300px' }} />
<Button type="primary">submit</Button>
</div>
<List
bordered
dataSource={this.state.list}
renderItem={item= > <List.Item>{item}</List.Item>} / ></div>)}}export default TodoList;
Copy the code
The browser displays:
๐งต 3. Compile actions and Reducer – add functions
1. Content transformation of the main page
Next, we use action and Reducer to pass data back and forth from this component. First, let’s transform the todolist.js file. The specific code is as follows:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store';
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState()
this.handleInputChange = this.handleInputChange.bind(this)
this.handleStoreChange = this.handleStoreChange.bind(this)
this.handleBtnClick = this.handleBtnClick.bind(this)
store.subscribe(this.handleStoreChange)
}
render() {
return (
<div style={{marginTop: '10px', marginLeft: '10px'}} >
<div>
<Input
value={this.state.inputValue}
placeholder="todo info"
style={{ width: '300px', marginRight: '10px'}}
onChange={this.handleInputChange}
/>
<Button type="primary" onClick={this.handleBtnClick}>submit</Button>
</div>
<List
style={{marginTop: '10px', width: '300px'}}
bordered
dataSource={this.state.list}
renderItem={item= > <List.Item>{item}</List.Item>} / ></div>)}handleInputChange(e) {
In React, action is the form of an object
// Type tells react to change the input value for me. This value is the following value, e.target.value
const action = {
type: 'change_input_value'.value: e.target.value
}
store.dispatch(action)
// console.log(e.target.value)
}
handleStoreChange() {
// Call the store. GetState method and fetch the data from the store again.
// Then call setState to replace the current store data
this.setState(store.getState())
}
handleBtnClick() {
const action = {
type: 'add_todo_item'
}
store.dispatch(action)
}
}
export default TodoList;
Copy the code
Let’s examine the above code. First, each action is bound to the corresponding event, and then, within the event, the action is created. In the case of the created action, it tells React to help the action change the value to which it is bound.
Finally, the action is done, and its data needs to be stored in a store. The action is then processed via store.dispatch(Action), passing the action’s data to the store.
Change the data in the action
The initial value of the action is fixed. However, reducer is needed when we want to modify the value in the action. Now, let’s modify the reducer. Js file so that the input box can input values freely and add the list after clicking the submit button. The specific code is as follows:
const defaultStore = {
inputValue: '123'.list: [1.2]};Reducer can receive state, but must not modify state
const reducer = (state = defaultStore, action) = > {
if (action.type === 'change_input_value') {
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
if (action.type === 'add_todo_item') {
const newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue);
newState.inputValue = ' ';
console.log(newState);
return newState;
}
return state;
}
export default reducer;
Copy the code
3. Store data transformation
Next, let’s take a look at the contents of index.js in the Store folder. We need to make a simple transformation of it, the specific code is as follows:
import { createStore } from "redux";
import reducer from './reducer';
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
Copy the code
In addition to the reducer, we pass window.__redux_devTools_extension__ &&window.__redux_devTools_extension__ () and call this method.
Finally, let’s take a look at the browser display:
๐งถ 4. Use Redux to achieve the deletion function of TodoList
1. Bind components to events
Now that we’ve done the add function, let’s move on to the delete function, which deletes the data for each click. Todolist.js: todolist.js: todolist.js: todolist.js: todolist.js: todolist.js: todolist.js
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store';
class TodoList extends Component {
constructor(props) {
// Omit the previous code here
}
render() {
return({/* omit the above code */}
<List
style={{marginTop: '10px'.width: '300px'}}
bordered
dataSource={this.state.list}
renderItem={(item, index) = > <List.Item onClick={this.handleItemDelete.bind(this, index)} >{item}</List.Item>}
/>
</div>
)
}
// Omit the previous code here
handleItemDelete(index) {
const action = {
type: 'delete_todo_item', index } store.dispatch(action); }}export default TodoList;
Copy the code
2. Data communication was conducted in reducer
Then, we communicate the data in reducer. Js file. The specific code is as follows:
const defaultStore = {
inputValue: '123'.list: [1.2]};Reducer can receive state, but must not modify state
const reducer = (state = defaultStore, action) = > {
// Omit the previous code here
if (action.type === 'delete_todo_item') {
const newState = JSON.parse(JSON.stringify(state));
newState.list.splice(action.index, 1);
return newState;
}
return state;
}
export default reducer;
Copy the code
Now, let’s take a look at the browser display:
๐ logical induction
1. Split ActionTypes
In todolist.js above, you can see that we’re going to be doing actions a lot. At the same time, if we make a slight mistake in type, the sorting process is always difficult to locate.
So, one of the things we’re going to do is split ActionTypes.
First, we’ll add a new file in the Store folder called actiontypes.js. The specific code is as follows:
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';
Copy the code
Second, modify the content under Todolist.js. The specific code is as follows:
import {
CHANGE_INPUT_VALUE,
ADD_TODO_ITEM,
DELETE_TODO_ITEM
} from './store/actionTypes'
class TodoList extends Component {
handleInputChange(e) {
const action = {
type: CHANGE_INPUT_VALUE,
value: e.target.value
}
store.dispatch(action)
}
handleStoreChange() {
this.setState(store.getState())
}
handleBtnClick() {
const action = {
type: ADD_TODO_ITEM
}
store.dispatch(action)
}
handleItemDelete(index) {
const action = {
type: DELETE_TODO_ITEM, index } store.dispatch(action); }}export default TodoList;
Copy the code
Finally, reform the reducer.js file. The specific code is as follows:
import {
CHANGE_INPUT_VALUE,
ADD_TODO_ITEM,
DELETE_TODO_ITEM
} from './actionTypes';
const defaultStore = {
inputValue: '123'.list: [1.2]};const reducer = (state = defaultStore, action) = > {
if (action.type === CHANGE_INPUT_VALUE) {
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
if (action.type === ADD_TODO_ITEM) {
const newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue);
newState.inputValue = ' ';
console.log(newState);
return newState;
}
if (action.type === DELETE_TODO_ITEM) {
const newState = JSON.parse(JSON.stringify(state));
newState.list.splice(action.index, 1);
return newState;
}
return state;
}
export default reducer;
Copy the code
By integrating change_input_value, add_todo_item, and delete_todo_item into actiontypes.js, we can make it easier to debug if we get a letter wrong.
2. Use actionCreator to create action
As you can see from todolist.js above, for several bound events, we always have to create actions frequently. Repetitive actions are the most taboo thing in programs. Therefore, we need to use Action Creator to unify the management of actions and make the logic more unified and complete.
First, we will create a new file in the Store folder called actionActionine.js. The specific code is as follows:
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from "./actionTypes";
export const getInputChangeAction = (value) = > ({
type: CHANGE_INPUT_VALUE,
value: value
});
export const getAddItemAction = (value) = > ({
type: ADD_TODO_ITEM
});
export const getDeleteItemAction = (index) = > ({
type: DELETE_TODO_ITEM,
index: index
});
Copy the code
Moving on, let’s transform Todolist.js. The specific code is as follows:
import { getInputChangeAction, getAddItemAction, getDeleteItemAction } from './store/actionCreators'
class TodoList extends Component {
handleInputChange(e) {
const action = getInputChangeAction(e.target.value);
store.dispatch(action)
}
handleBtnClick() {
const action = getAddItemAction();
store.dispatch(action)
}
handleItemDelete(index) {
constaction = getDeleteItemAction(index); store.dispatch(action); }}export default TodoList;
Copy the code
The final logic is more unified by separating the actions from the action into actionCreators. Js.
๐ some summary of Redux
At this point, we sum up some of the above knowledge points as follows
1. Three principles of Redux design and use
The design and use of Redux follows three principles:
store
Must be unique ๐ that is, there must be only one in the entire applicationstore
๏ผ- only
store
Be able to change your content ๐ i.estore
notreducer
To renew, butstore
Get thereducer
After the data, their own data for an update; So let’s go back to the topreducer.js
File,react
In, is not allowedState. inputValue === a value
Something like that, which means you can’t assign it directly. Reducer
Must be pure function ๐ so calledPure functionsThat is, given a fixed input, there must be a fixed output without any side effects. Let’s go back to the one above usreducer.js
The document, as you can see,state
It’s fixed,action
Is also fixed, so the final returnnewState
Nature means fixed.
Redux’s core API
Let’s review some of Redux’s core apis. Take a look at the picture below:
Now let’s review what these core apis do. Details are as follows:
- CreateStore – can help us create one
store
๏ผ - Store. Dispatch — —
dispatch
Methods to help us distributeaction
At the same time, thisaction
Will pass tostore
๏ผ - Store. GetState — —
getState
Methods help us get all the data; - Store. The subscribe — —
subscribe
Help us subscribestore
Change, as long asstore
Change,store.subscribe
The received callback function is executed.
๐ 7. Separation of advanced components
1. Separation of UI components and container components
In the code above, we are almost done with TodoList. However, did you notice that in todolist.js, the rendering of the page and the logic of the page are put together?
In real development, we tend to separate UI components and container components directly. The UI component is dedicated to rendering the page, while the container component is responsible for the logic of the page.
So let’s break it up. First, we will now add a new file in the SRC folder named todolistui.js. The specific code is as follows:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
class TodoListUI extends Component {
render() {
return (
<div style={{ marginTop: '10px', marginLeft: '10px' }}>
<div>
<Input
value={this.props.inputValue}
placeholder="todo info"
style={{ width: '300px', marginRight: '10px'}}onChange={this.props.handleInputChange}
/>
<Button type="primary" onClick={this.props.handleBtnClick}>submit</Button>
</div>
<List
style={{ marginTop: '10px', width: '300px'}}bordered
dataSource={this.props.list}
renderItem={(item, index) = > <List.Item onClick={()= > { this.props.handleItemDelete(index) }}>{item}</List.Item>} / ></div>)}}export default TodoListUI;
Copy the code
Moving on, let’s modify the contents of the todolist.js file. The specific code is as follows:
import React, { Component } from 'react';
import store from './store';
import { getInputChangeAction, getAddItemAction, getDeleteItemAction } from './store/actionCreators';
import TodoListUI from './TodoListUI';
class TodoList extends Component {
constructor(props) {
super(props);
this.state = store.getState()
this.handleInputChange = this.handleInputChange.bind(this)
this.handleStoreChange = this.handleStoreChange.bind(this)
this.handleBtnClick = this.handleBtnClick.bind(this)
this.handleItemDelete = this.handleItemDelete.bind(this)
store.subscribe(this.handleStoreChange)
}
render() {
return (
<TodoListUI
inputValue={this.state.inputValue}
list={this.state.list}
handleInputChange={this.handleInputChange}
handleBtnClick={this.handleBtnClick}
handleItemDelete={this.handleItemDelete}
/>)}handleInputChange(e) {
const action = getInputChangeAction(e.target.value);
store.dispatch(action)
}
handleStoreChange() {
this.setState(store.getState())
}
handleBtnClick() {
const action = getAddItemAction();
store.dispatch(action)
}
handleItemDelete(index) {
constaction = getDeleteItemAction(index); store.dispatch(action); }}export default TodoList;
Copy the code
As you can see, we pulled out the content of the page and put it in the todolistui.js file to do only rendering. Thus, we have successfully separated the UI component from the logical component.
Stateless components
Now that we have the UI component, let’s look at another component, the stateless component. The so-called stateless component is the whole page no logic, only a render function, we can call it a stateless component.
How w do you define stateless components?
We can define a function that takes a parameter, props. The todolistui.js file contains the following code:
import React from 'react';
import { Input, Button, List } from 'antd';
const TodoListUI = (props) = > {
return (
<div style={{ marginTop: '10px', marginLeft: '10px' }}>
<div>
<Input
value={props.inputValue}
placeholder="todo info"
style={{ width: '300px', marginRight: '10px'}}onChange={props.handleInputChange}
/>
<Button type="primary" onClick={props.handleBtnClick}>submit</Button>
</div>
<List
style={{ marginTop: '10px', width: '300px'}}bordered
dataSource={props.list}
renderItem={(item, index) = > <List.Item onClick={()= > { props.handleItemDelete(index) }}>{item}</List.Item>} / ></div>)}export default TodoListUI;
Copy the code
When a normal function only has the render function, we can simply replace it with a stateless component. So why do I make this substitution?
If we change it to just one function, then the program only needs to run that function, and that’s all it needs to do. In other words, if we use class, then the object behind the class has a lot of life cycle functions and so on, which is not so pure. Therefore, we define stateless components in such a way as to make components more pure.
๐ฉ Redux initiates an asynchronous request
1. Send asynchronous request data in Redux
Often in real projects, we need to interface data with back-end requests and send AJAX requests. React requests to the backend interface.
First we request data under todolist.js. The specific code is as follows:
import { getInputChangeAction, getAddItemAction, getDeleteItemAction, initListAction } from './store/actionCreators';
class TodoList extends Component {
componentDidMount() {
axios.get('./list.json').then((res) = > {
const data = res.data;
constaction = initListAction(data); store.dispatch(action); }}})Copy the code
Next, modify the actiontypes.js code. Details are as follows:
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';
export const INIT_LIST_ACTION = 'init_list_action';
Copy the code
Moving on, we packaged the action in actionActionine.js. The specific code is as follows:
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION } from "./actionTypes";
export const getInputChangeAction = (value) = > ({
type: CHANGE_INPUT_VALUE,
value: value
});
export const getAddItemAction = (value) = > ({
type: ADD_TODO_ITEM
});
export const getDeleteItemAction = (index) = > ({
type: DELETE_TODO_ITEM,
index: index
});
export const initListAction = (data) = > ({
type: INIT_LIST_ACTION,
data: data
})
Copy the code
Finally, modify the reducer.js code. The specific code is as follows:
import {
CHANGE_INPUT_VALUE,
ADD_TODO_ITEM,
DELETE_TODO_ITEM,
INIT_LIST_ACTION
} from './actionTypes';
const defaultStore = {
inputValue: '123'.list: [1.2.3]};Reducer can receive state, but must not modify state
const reducer = (state = defaultStore, action) = > {
if (action.type === CHANGE_INPUT_VALUE) {
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
if (action.type === INIT_LIST_ACTION) {
const newState = JSON.parse(JSON.stringify(state));
newState.list = action.data;
return newState;
}
if (action.type === ADD_TODO_ITEM) {
const newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue);
newState.inputValue = ' ';
console.log(newState);
return newState;
}
if (action.type === DELETE_TODO_ITEM) {
const newState = JSON.parse(JSON.stringify(state));
newState.list.splice(action.index, 1);
return newState;
}
return state;
}
export default reducer;
Copy the code
Thus, we have implemented the AJAX request via AXIos, please let it get the data.
2. Redux-thunk middleware
(1) What problems are solved
In the example above, we successfully made a request for the interface’s data. The above case is a relatively simple example, but often in the actual scene we encounter, are more complex examples.
Therefore, we want to move it to another file for management when we have asynchronous requests or very complex logic.
This is where redux-Thunk middleware is used for problem solving. How to use redux-Thunk middleware?
(2) How to use it
Step 1: Install redux-Thunk. Specific commands are as follows:
npm i redux-thunk -D
Copy the code
Step 2: Introduce redux-thunk. Redux-devtools is often used to debug the project store during actual debugging. But what if we were to introduce both Redux-DevTools and redux-Thunk middleware? In the store | index. Js file for processing. The specific code is as follows:
The compose function comes from redux
import { createStore, applyMiddleware, compose } from "redux";
import reducer from './reducer';
import thunk from 'redux-thunk';
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(thunk)
);
const store = createStore(
reducer,
enhancer
);
export default store;
Copy the code
With this form of coding, our store supports Both DevTools for Windows, which means we can debug the store, and redux-thunk.
Step 3: Pull out the asynchronous logic. Let’s start by modifying the todolist.js code. Details are as follows:
import { getTodoList, getInputChangeAction, getAddItemAction, getDeleteItemAction } from './store/actionCreators';
class TodoList extends Component {
componentDidMount() {
// Action is a function
const action = getTodoList();
// Only with thunk can action be passed as a functionstore.dispatch(action); }}Copy the code
Next, modify the actionactionine.js code. The specific code is as follows:
// getTodoList is a function
// A function generated in this form can receive the dispatch method directly
export const getTodoList = () = > {
return (dispatch) = > {
axios.get('./list.json').then((res) = > {
const data = res.data;
// Action is an object
constaction = initListAction(data); dispatch(action); }}})Copy the code
Let’s explain the above two pieces of code as follows:
After configuring the Redux-Thunk environment, it allows us to write asynchronous code in the action! Why do you say that?
- Before we were creating
action
Can only be oneJS objectAnd now, when usedredux-thunk
And then, even thoughgetTodoList()
The returnednotAn objectbutA function can also passstore.dispatch()
To send the function tostore
. - Then why can befunctionWhat about sending it out? Because of using it
redux-thunk
ใ
Moving on, we’ll talk about specific implementation steps ๐ :
- First, let
TodoList.js
In thestore
To performaction
Function. And thisaction
The delta function comes from thetaactionCreators.js
In thegetTodoList()
ใ - for
getTodoList()
Say, what it has to do is goRequest JSON dataandGet json data. - And once you’ve got the data, then you have to change
store
The data inside, then you have to create one firstaction
theaction
Provide forstore.dispatch()
Call. But,store.dispatch()
How do you get it? It will be automatically received in the function that we returnstore.dispatch()
Methods. So, just passdispatch(action)
That will beaction
Just hand out the pies. - In other words,
redux-thunk
It makes us createaction
Or supportaction
When, is onefunctionIn the form.
(3) Why redux-thunk?
After reading the above explanation, you should know the magic of Redux-thunk. So why redux-thunk? ๐
If asynchronous functions are used in the life cycle of a component, the logic of the component becomes more complex and the content of the component becomes more and more. As a result, complex asynchronous logic is often split off and managed separately. Now, with the help of the Redux-Thunk middleware, we split the asynchronous logic into actionActionine.js for separate management. As a result, the code is more standardized and unified.
(4) What is Redux-Thunk middleware?
With that in mind, let’s go back to the middleware roots and talk about the principles behind the Redux-Thunk middleware.
The so-called middleware is definitely the middle of who and who. Let’s start with a picture:
The middle of the Redux middleware is between the Action and the Store.
As we said earlier, in Redux, an action can only be an object, and because it is an object, it is sent directly to the store. Now, when we use redux-thunk, action can be a function. So why can it be a function?
As you can see from the above figure, the Action sends the data to the Store via dispatch. And there’s a dispatch method between an action and a store, so middleware is essentially a wrapper and an upgrade to the Dispatch method.
For the original Dispatch method, it would receive a JS object and pass it to the Store.
But if we pass a function, then the dispatch is upgraded. Instead of passing functions directly to the Store, Dispatch executes the corresponding functions through the Redux-Thunk middleware and calls the Store when it needs to be called.
๐ผ Other middleware for Redux
1, the story – the logger
There are a lot of middleware in Redux. For example, the Redux-Logger can log every time an action is sent. So how does it record?
Every time it calls an action, it passes the action to the Store through the Dispatch method, and then we can upgrade the Dispatch so that not only does the Dispatch pass the action to the Store, And before each delivery, we printed it console.log, so we wrote a middleware for Redux-Logger that printed the action to our console when we sent it.
2, the story – saga
(1) What is Redux-saga
In today’s projects, the popular middleware not only includes Redux-Thunk, Redux-Logger, but also Reudx-Saga.
Reudx-saga is also a middleware solution to asynchronous problems in React. Unlike Redux-Thunk, redux-Thunk uses asynchronous operations in actions. The idea behind Redux-Saga is to separate asynchronous logic and manage it in a separate file. How to use the redux-Saga middleware?
(2) How to use Redux-Saga
We upgraded the TodoList component above. First is the store | index. Js file. The specific code is as follows:
import { createStore, applyMiddleware, compose } from "redux";
import reducer from './reducer';
import createSagaMiddleware from 'redux-saga';
import todoSagas from './sagas';
const sagaMiddleware = createSagaMiddleware()
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(sagaMiddleware(sagaMiddleware));
const store = createStore(reducer, enhancer);
sagaMiddleware.run(todoSagas);
export default store;
Copy the code
In this file, it’s all about getting the basic configuration right. The main points to note here are:
- The introduction of
createSagaMiddleware
๏ผ - And then use
const sagaMiddleware = createSagaMiddleware()
To introduce it; - use
apllyMiddleware
To use this middleware; - After using the middleware, we created the
saga.js
ใ
Next we create saga.js in the Store folder. The specific code is as follows:
import { takeEvery, put } from 'redux-saga/effects';
import { initListAction } from './actionCreators';
import { GET_INIT_LIST } from './actionTypes';
import axios from 'axios';
function getInitList() {
try {
const res = yield axios.get('./list.json');
const action = initListAction(res.data);
yield put(action);
} catch (e) {
console.log('List. json network request failed'); }}function* mySaga() {
// Use takeEvery to capture every action sent out
yield takeEvery(GET_INIT_LIST, getInitList);
}
export default mySaga;
Copy the code
There are a few things to note about saga.js:
- in
saga.js
Inside, you have to export onegenerator
Function, and inside this function, we’ve written some logic. The logic is, when we receiveaction
Type isGET_INIT_LIST
“, then we will executegetInitList
This method. getInitList()
A method is a function that will fetch the data for us, and then create a new one from that dataaction
And put thisaction
throughyield put(action)
The way, sent tostore
ใ
Now let’s look at actiontypes.js. The specific code is as follows:
// CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION
export const GET_INIT_LIST = 'get_init_list';
Copy the code
Next, we go to Todolist.js. The specific code is as follows:
import { getInputChangeAction, getAddItemAction, getDeleteItemAction, getInitList } from './store/actionCreators';
class TodoList extends Component {
// omit n content here
componentDidMount() {
constaction = getInitList(); store.dispatch(action); }}export default TodoList;
Copy the code
Finally, the store | actionCreators. Js. The specific code is as follows:
import { GET_INIT_LIST, CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION } from "./actionTypes";
// omit n content here
export const getInitList = () = > ({
type: GET_INIT_LIST
});
Copy the code
In todolist.js, we create an action and send it to the store.
๐ต ten, React – story
1. What is React-redux
After react, we learned redux. So if you combine them, what is react-redux?
In fact, it’s a third-party module that makes it easier to use Redux in React.
Use of React-redux
(1) Install react-redux
Similarly, let’s look at the use of React-Redux using the TodoList component as an example. First, create a react project and install react-redux. Specific commands are as follows:
npm install react-redux
Copy the code
(2) Project catalog
Let’s start with the project catalog. The details are as follows:
(3) Core content
The first step is to mount the TodoList component to the page. Under the SRC | index. Js file content is as follows:
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
import { Provider } from 'react-redux';
import store from './store';
const App = (
// Indicates that all components of the Provider have the ability to obtain the store
<Provider store={store}>
<TodoList />
</Provider>
)
ReactDOM.render(App, document.getElementById('root'));
Copy the code
Provider is the first core API provided by React. It is designed to show that all components in the Provider have the ability to access the Store.
The second step is to write the SRC | TodoList. Js. The specific code is as follows:
import React from 'react';
import { connect } from 'react-redux';
const TodoList = (props) = > {
const { inputValue, list, changeInputValue, handleClick, handleDelete } = props;
return (
<div>
<div>
<input value={inputValue} onChange={ changeInputValue} / >
<button onClick={ handleClick} >submit</button>
</div>
<ul>
{
list.map((item, index) => {
return <li onClick={handleDelete} key={index}>{ item }</li>})}</ul>
</div>)}const mapStateToProps = (state) = > {
return {
inputValue: state.inputValue,
list: state.list
}
}
// store, dispatch, props
const mapDispatchToProps = (dispatch) = > {
return {
changeInputValue(e) {
const action = {
type: 'change_input_value'.value: e.target.value
};
// console.log(action.value)
dispatch(action);
},
handleClick() {
const action = {
type: 'add_item'
}
dispatch(action);
},
handleDelete(){}}}// Let TodoList connect to store
// TodoList is a UI component. Connect combines this UI component with the preceding business logic
export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
Copy the code
In the code above, we notice connect in React-Redux.
Who is connecting with whom? TodoList connects to Store. To connect them, they need a mapping, and that mapping is inside mapStateToProps.
In mapStateToProps, state refers to the data in store. If the data in store is mapped to props, then we can use this.props. XXX to get the data in store.
Step 3 Create a Reducer. In SRC | store | reducer, js, under specific code is as follows:
const defaultState = {
inputValue: ' '.list: []}export default (state = defaultState, action) => {
if (action.type === 'change_input_value') {
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
if (action.type === 'add_item') {
const newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue);
newState.inputValue = ' ';
return newState;
}
return state;
}
Copy the code
Put the data in the store into the reducer for recording.
Step 4, pass the Reducer to the store. In SRC | store | index, js, under specific code is as follows:
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer);
export default store;
Copy the code
We make a deep copy of the content stored in the Reducer and send it back to the store. In this way, a closed loop of data transmission is formed.
Finally, let’s take a look at the browser display:
React-redux is much more intuitive and concise to use than middleware. In a real world project, either redux middleware or React-Redux is worth using for state management.
It is important to note that redux middleware and React-Redux have different points in their use. As for which type to use in a project, it depends on the current project scenario.
๐ฆ 11. Conclusion
In the previous article, we covered three principles for Redux design and use, as well as some of the core apis in Redux. In addition, we also learned about the middleware of Redux, Redux-Thunk and Redux-Saga. You also learned about another aspect of state management, React-redux.
That’s it for Redux! I wonder if you have a new understanding of Redux?
๐ฃ Egg One More Thing
Previous recommendation
๐ Use React to implement todoList
๐ React on the surface? Five knowledge points take you to comb the advanced knowledge
) Introduction
- Pay attention to the public account Monday laboratory, the first time to pay attention to quality articles, more “offer to come” interview column for you to unlock ~
- If you think this article is helpful to you, you might as well like to support yo ~~๐
- That’s all for this article! See you next time! ๐ ๐ ๐