In this paper, react Family barrel products are very refined to extract the core content, the essence level is comparable to essential oil. Ladies and gentlemen, since you are here, take a seat for the guest officer. Somebody, give the guest officer tea ~~
redux
preface
First of all, this article requires you to have some knowledge of JS and React. If you don’t know react, you should read it first. A complete summary (Rush)
React has props and state:
- Props means properties that are distributed by the parent
- State means the state that can be managed internally by the component, and the entire React does not have the ability to trace data upward. This is the one-way data flow of React
This means that if it is a very complex application data status, more found the React cannot let two components communicate with each other, using each other’s data, the React of transfer data through the hierarchy of this method is very uncomfortable, this time, the urgent need for a mechanism to put all of the state on the top of the component, Having the flexibility to distribute all the states as needed to all the components, yes, that’s Redux
Introduction to the
- Redux was created to provide “predictable state management” for React applications.
- Redux stores the entire application state (that is, data) in a single place called a Store
- There’s a state tree in this store.
- The only way for a component to change state is by calling the Store’s Dispatch method, which triggers an action that is processed by the corresponding Reducer, and state completes the update
- Components can dispatch actions to stores instead of notifying other components directly
- Other components can refresh their views by subscribing to states in the Store
Using the step
- Create the reducer
- A single reducer can be used, or multiple reducers can be combined into a reducer, that is:
combineReducers()
- Action puts the state into the Reucer processing function after issuing the command, returns the new state, and processes the state
- A single reducer can be used, or multiple reducers can be combined into a reducer, that is:
- Create an action
- State is not accessible to users, but can only be triggered by the view. Therefore, this action can be understood as an instruction, and there are as many instructions as need to be issued
- Action is an object and must have a parameter called type that defines the action type
- Create a store using the createStore method
- Store can be understood as a total factory with multiple processing machines
- Subscribe, dispatch, and getState methods are provided.
Step by step.
The sequence number of the above steps will be marked in the relevant code
npm install redux -S / / installation
import { createStore } from 'redux' / / introduction
const reducer = (state = {count: 0}, action) = >{-- -- -- -- -- -- -- -- -- -- > (1)switch (action.type){
case 'INCREASE': return {count: state.count + 1};
case 'DECREASE': return {count: state.count - 1};
default: returnstate; }}constThe actions = {-- -- -- -- -- -- -- -- -- -- > 2 happens:(a)= > ({type: 'INCREASE'}),
decrease: (a)= > ({type: 'DECREASE'})}conststore = createStore(reducer); -- -- -- -- -- -- -- -- -- -- > (3) store. The subscribe ((a)= >
console.log(store.getState())
);
store.dispatch(actions.increase()) // {count: 1}
store.dispatch(actions.increase()) // {count: 2}
store.dispatch(actions.increase()) // {count: 3}
Copy the code
I drew a very simple flowchart to understand the redux workflow
react-redux
React (props) If the store is integrated directly into the React props, all the sub-components need to be able to access the props.
Store ={store}> <App /> </ store >Copy the code
Isn’t that ok? That’s react-redux. Redux provides the React binding library. It is efficient and flexible.
React Redux distinguishes components into container components and UI components
- The former handles logic
- The latter is only responsible for display and interaction, and does not handle logic internally. The state is completely controlled externally
Two core
-
Provider
Look at the top component of my code up there four words. Yes, you guessed right. The top component is the Provider. We usually wrap the top component in the Provider component, so that all components can be controlled by react-Redux, but stores must be placed as arguments in the Provider component
<Provider store = {store}> <App /> <Provider> Copy the code
The purpose of this component is for all components to have access to the data in Redux.Copy the code
-
connect
This is the hard part of React-Redux, so let’s explain it in detail
First, remember this line of code:
connect(mapStateToProps, mapDispatchToProps)(MyComponent) Copy the code
mapStateToProps
Map state to props (); map data from Redux to props ();
Here’s an example:
const mapStateToProps = (state) => {
return{/ / prop: state. | means XXX will be one of the state data is mapped to the props in the foo: state. The bar}}Copy the code
Then you can render using this.props. Foo
class Foo extends Component {
constructor(props){
super(props);
}
render() {return<div>this.props. Foo </div>)}} foo = connect()(foo);export default Foo;
Copy the code
Then you can finish rendering
mapDispatchToProps
The word translates as props for all kinds of dispatches that you can use directly
Const mapDispatchToProps = (dispatch) => {// The default pass parameter is dispatchreturn {
onClick: () => {
dispatch({
type: 'increatment'}); }}; }Copy the code
class Foo extends Component {
constructor(props){
super(props);
}
render() {return(<button onClick = {this.props. OnClick}> increase</button>)}} Foo = connect()(Foo);export default Foo;
Copy the code
You can call dispatch directly with this.props. OnClick, so you don’t need to store
That concludes the basic introduction to React-Redux
redux-saga
If the original redux workflow is followed, reducer modification state will be directly triggered when an action is generated in the component. Reducer is a pure function, that is, no asynchronous operation can be performed in reducer.
In practice, an asynchronous task needs to be completed before the actions in the components enter the reducer, such as sending ajax requests and receiving data, and then entering the reducer. Obviously, native Redux does not support this operation
There is an urgent need for middleware to handle this business scenario, and redux-Saga is by far the most elegant solution
Interpretation of the core
1. Saga auxiliary function
Redux-saga provides helper functions for deriving tasks when certain actions are initiated to the Store. I’ll start with two helper functions: takeEvery and takeLatest
-
takeEvery
TakeEvery is just like a dishwasher on an assembly line. Once you get a dirty plate, he will directly perform the following dishwashing function. Once you hire this dishwasher, he will always perform this work and will never stop the monitoring process of receiving dishes and trigger the dishwashing function
For example, every time a button is clicked to Fetch data, we issue a FETCH_REQUESTED action. We want to process this action by starting a task to get some data from the server, something like this
window.addEventLister('xxx',fn)
Copy the code
When dispatch XXX is executed, the fn method is executed,
First we create a task that will execute an asynchronous action (fn above) :
// put: you can say put equals dispatch; // call: an asynchronous function that blocks until the next function is run. // await in async! But it's much more intuitive! import { call, put } from'redux-saga/effects'
export function* fetchData(action) {
try {
const apiAjax = (params) => fetch(url, params);
const data = yield call(apiAjax);
yield put({type: "FETCH_SUCCEEDED", data});
} catch (error) {
yield put({type: "FETCH_FAILED", error}); }}Copy the code
The above task is then started each time a FETCH_REQUESTED action is triggered, which means that the above task will be executed each time an action named FETCH_REQUESTED is triggered
import { takeEvery } from 'redux-saga'
function* watchFetchData() {
yield* takeEvery("FETCH_REQUESTED", fetchData)
}
Copy the code
Note: The above takeEvery function can be replaced with the following
function* watchFetchData() {
while(true){
yield take('FETCH_REQUESTED'); yield fork(fetchData); }}Copy the code
-
takeLatest
In the example above, takeEvery allows multiple fetchData instances to be started at the same time, and ata certain point we can start a new fetchData task, even though one or more fetchData instances have not yet been completed
If we just want the response to the latest request (for example, always show the latest version of the data), we can use the takeLatest helper function
import { takeLatest } from 'redux-saga'
function* watchFetchData() {
yield* takeLatest('FETCH_REQUESTED', fetchData)
}
Copy the code
Unlike takeEvery, takeLatest allows only one fetchData task to be executed at any one time, and that task is the last one to be started. If there is already a task in progress, that task is automatically cancelled
2, the Effect of Creators
The Redux-Saga framework provides a number of functions for creating effects, so let’s take a quick look at some of the most commonly used in development
- take(pattern)
- put(action)
- call(fn, … args)
- fork(fn, … args)
- select(selector, … args)
take(pattern)
The take function, which can be understood as listening for future actions, creates a command object that tells middleware to wait for a specific action, and the Generator pauses until an action matching pattern is initiated. In other words, Take is a blocking effect
Usage:
function* watchFetchData() {
while(true) {// listen on onetypefor'FETCH_REQUESTED'FetchData (fetchData), which executes the following yield fork(fetchData) statement:'FETCH_REQUESTED'); yield fork(fetchData); }}Copy the code
put(action)
Put is an effect that sends an action. You can think of it simply as a Dispatch function in the Redux framework. When an action is put, the reducer will calculate the new state and return it
Usage:
export function* toggleItemFlow() {
letList = [] // Send onetypefor'UPDATE_DATA''data: list' yield PUT ({data: list 'yield put({type: actionTypes.UPDATE_DATA,
data: list
})
}
Copy the code
call(fn, … args)
Call is a function that can call other functions. It commands middleware to call fn (args). The fn function can be a Generator or a normal function that returns a Promise, and the call function is also a blocking effect
Usage:
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
export function* removeItem() {try {// The call function calls the delay function, which returns a promisereturn yield call(delay, 500)
} catch (err) {
yield put({type: actionTypes.ERROR})
}
}
Copy the code
fork(fn, … args)
Fork, like call, is used to call other functions, but fork is non-blocking. That is, after the yield fork(FN, ARgs) line is executed, the program will immediately execute the next line of code without waiting for the fn function to return the result. Execute the following statement
Usage:
import { fork } from 'redux-saga/effects'
export default function* rootSaga() {// The following four Generator functions are executed at once, Do not block execution yield fork(addItemFlow) Yield fork(removeItemFlow) Yield fork(toggleItemFlow) Yield fork(modifyItem)}Copy the code
select(selector, … args)
The select function instructs middleware to call a selector to getState data from a Store. You can also simply think of it as a redux framework function to getState data from a Store: store.getstate ()
Usage:
export function* toggleItemFlow() {// Select effect to get the list of 'getTodoList' on global statelet tempList = yield select(state => state.getTodoList.list)
}
Copy the code
A concrete example
**index.js **
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, applyMiddleware} from 'redux'
import createSagaMiddleware from 'redux-saga'
import rootSaga from './sagas'
import Counter from './Counter'
import rootReducer from './reducers'Const sagaMiddleware = createSagaMiddleware() // Creates a saga middleware instance // This statement creates a store in the same way as the two lines below // const store = createStore(reducers,applyMiddlecare(middlewares)) const createStoreWithMiddleware = applyMiddleware(middlewares)(createStore) const store = createStoreWithMiddleware(rootReducer) sagaMiddleware.run(rootSaga) const action =type => store.dispatch({ type })
function render() {
ReactDOM.render(
<Counter
value={store.getState()}
onIncrement={() => action('INCREMENT')}
onDecrement={() => action('DECREMENT')}
onIncrementAsync={() => action('INCREMENT_ASYNC')} />,
document.getElementById('root')
)
}
render()
store.subscribe(render)
Copy the code
sagas.js
import { put, call, take,fork } from 'redux-saga/effects';
import { takeEvery, takeLatest } from 'redux-saga'
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
function* incrementAsync() {yield call(delay, 1000); yield put({type: 'INCREMENT' });
}
export default function* rootSaga() {
// while(true){
// yield take('INCREMENT_ASYNC'); // yield fork(incrementAsync); Yield * takeEvery();"INCREMENT_ASYNC", incrementAsync)
}
Copy the code
reducer.js
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
case 'INCREMENT_ASYNC':
return state
default:
return state
}
}
Copy the code
As can be seen from the above code structure, the way of using Redux-Saga is relatively simple. Compared with the previous CounterApp of Redux framework, there is an additional Sagas file and reducers file is still used in the previous way
Redux-saga redux-saga
- Use the createSagaMiddleware method to create the Middleware for Saga, and when creating redux’s store, Use the applyMiddleware function to bind a saga Middleware instance to a store, and you can call the Saga Middleware run function to execute one or more Middleware instances.
- In The Middleware of Saga, you can use apis such as takeEvery or takeLatest to listen for an action. When an action is triggered, Saga can use call to initiate an asynchronous action. When the operation is complete, use the put function to trigger the action and update the state synchronously, thus completing the update of the entire state.
Redux, React-Redux, redux-Saga, Redux-Saga, Redux-Saga, Redux-Saga, Redux-Saga, Redux-Saga, Redux-Saga, Redux-Saga, Redux-Saga, Redux-Saga, Redux-Saga, Redux-Saga, Redux-Saga