The source of the Saga
We all know that technology is created to solve a problem or class of problems.
Saga is a feature enhancement of Redux, so let’s take a look at some of the problems with Redux alone.
1. Review of Redux basic knowledge
There are three core concepts in Redux:
Action
,Reducer
,Store
, Redux is dedicated to managing state.
This sentence seems hard to understand. What would we have done before Redux?
Before Redux, we manipulated and modified the data directly, and all the functional operations were handled in one place. As the application became more and more complex, the state of the application became very, very messy.
For example, if a restaurant wants fresh pork, the restaurant needs to raise the pigs themselves, slaughter the pigs, clean them and so on… Do everything by yourself. In addition to fresh pork, the restaurant also needs fresh vegetables, it also needs to plant vegetables, pick vegetables… In addition, the restaurant may also need seafood, accessories… And so on.
Being too busy is one thing, but it increases the cost of mistakes, such as picking vegetables and accidentally feeding them to pigs.
Hence the Redux:
Redux has three core concepts — Store, Action, and Reducer. Just like human organs, cells are differentiated, and specialized organs do specialized things efficiently and error-free.
In Redux, a Store dispatch Action is sent to the Reducer for processing. After the Reducer process, the result is sent back to update the Store.
Like this restaurant, it sent out an Action saying, “I want fresh pork and vegetables!” . The Reducer then received this Action and processed it, and finally brought fresh pork and green vegetables to the restaurant. The specific process was processed in the Reducer without the restaurant having any concern.
In this way, Redux helps us solve a state disorder problem. 👏
Saga is here
Then Redux alone is not enough! The Reducer has a lot to deal with: primary business logic, sideEffects (sideEffects such as asynchronously fetching data, accessing the browser cache, etc.).
In this example, the Reducer requires slaughter in addition to raising pigs, which also needs to be done after raising pigs. Likewise, Reducer should pick vegetables in addition to planting, but only when the vegetables grow up. This leaves the Reducer bloated and chaotic.
At this point, we needed to make further division of the Reducer functions, and at this point, Saga came out.
Saga is responsible for raising pigs, growing vegetables… Leave some of the asynchronous processing, reading from the cache, to Saga, technically called “sideEffects” :
How did Reducer know that the pigs were ready and the dishes were grown? Are you there every day?
Don’t need to! Saga was in charge of raising pigs and planting vegetables. When the pigs grew up and the vegetables were ready to be picked, she told Reducer: Slaughter is coming! Come to pick the vegetables! Reducer Receives information and performs related operations. There is no need to keep Reducer there every day to see if pigs grow up and if dishes can be eaten… (Will block the process.)
So how does Saga inform Reducer?
As we know, Reducer receives state and action as parameters, and then returns a new state through calculation. It is impossible to identify “notification information” in other formats. Therefore, when Saga informed the Reducer, it must also dispatch an Action to the Reducer for processing.
The origin of many grammatical rules is based on rules
The basics of Saga
Saga
Is a Redux middleware, meaning this thread can pass normalRedux Action
From the main application start, pause and cancel, it can access the fullRedux state
, it can also bedispatch Redux Action
. aSaga
Like a single thread in the application, it is solely responsible for handling side effects.
The middleware
Middleware is a non-business technology component. It acts as a mediator between the underlying logic and the business.
If the middleware is used, the program will be processed by the middleware and then returned to the specific business for processing. We can process the common parts of the application, or the parts that require asynchronous operations, such as user logins, data requests, etc., in middleware.
In Redux, when we dispatch a Redux Action, Saga, as the processing middleware, can receive the Action for relevant processing, and then send the relevant processing to the corresponding Reducer for processing.
Saga core API
1. Auxiliary functions
takeEvery
For example, each time we click the “Increment 1 after 1 second” button, we initiate an incrementAsync action.
First we create a task that will execute an asynchronous action:
import { delay, put } from 'redux-saga/effects'
function* incrementAsync() {
/ / delay 1 s
yield delay(1000)
yield put({
type: 'increment'})}Copy the code
The above task is then started each time incrementAsync Action is initiated.
import { takeEvery } from 'redux-saga'
function* watchIncrementAsync() {
yield takeEvery('incrementAsync', incrementAsync)
}
Copy the code
takeLatest
In the above example, takeEvery is executed every time an “incrementAsync” action is initiated. If we only want the response to the latest request, we can use the takeLatest helper function
import { takeLatest } from 'redux-saga'
function* watchIncrementAsync() {
yield takeLatest('incrementAsync', incrementAsync)
}
Copy the code
2, the Effect of Creators
The Redux-Saga framework provides many functions for creating an effect. Here are some of the most commonly used in development:
- take(pattern)
- put(action)
- fork(fn, … args)
The take(pattern) take function, which can be understood as listening for future actions, creates a command object that tells middleware to wait for a specific action until one matching pattern is initiated.
Put (action) The put function 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 calculates the new state and returns it.
function* incrementAsync() {
/ / delay 1 s
yield delay(1000)
yield put({
type: 'increment'})}Copy the code
fork(fn, … The fork function is used to call other functions, but the fork function is non-blocking, that is, the yield fork(fn, ARgs) line is immediately followed by the next line of code.
Story – Saga syntax
With Saga’s syntax behind us, let’s take a look at the syntax Redux uses with Saga:
1. Create the simplestSaga
// saga.js
function* helloSaga() {
console.log('Hello saga! ')}export default helloSaga
Copy the code
2, theSaga
Used as middleware
The createSagaMiddleware function is used to create a Saga middleware:
import createSagaMiddleware from 'redux-saga'
const sagaMiddleware = createSagaMiddleware()
Copy the code
3,Redux
How to Add Middleware
The applyMiddleware function is used to apply saga middleware:
import { createStore, applyMiddleware } from 'redux'
const store = createStore(reducer,
applyMiddleware(sagaMiddleware)
)
Copy the code
4, run,
import helloSaga from './saga'
sagaMiddleware.run(helloSaga)
Copy the code
Complete 🌰 code sample
myComponent.js
import React from 'react'
import ReactDOM from 'react-dom'
class MyComponent extends React.Component {
render() {
return (
<div className="index">
<p>{this.props.count}</p>
<button onClick={this.props.increment}>Add 1</button>
<button onClick={this.props.incrementAsync}>Add 1 after 1 second</button>
</div>
)
}
}
export default MyComponent
Copy the code
reducer.js
export default function reducer(state = {
count: 10
}, action) {
switch (action.type) {
case 'increment': {
return {
count: state.count + 1}}default:
return state
}
}
Copy the code
App.js
import { connect } from 'react-redux'
import MyComponent from './myComponent'
// Map Redux state to component props
function mapStateToProps(state) {
console.log('state', state)
return {
count: state.count
}
}
// Map Redux actions to component props
function mapDispatchToProps(dispatch) {
return {
increment: () => dispatch({
type: 'increment'
}),
incrementAsync: () => dispatch({
type: 'incrementAsync'
})
}
}
// Connected Component
const App = connect(
mapStateToProps,
mapDispatchToProps
)(MyComponent)
export default App
Copy the code
main.jsx
import "regenerator-runtime/runtime"
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import createSagaMiddleware from 'redux-saga'
import rootSaga from './saga'
import App from './App'
import reducer from './reducer'
const sagaMiddleware = createSagaMiddleware()
// Store
const store = createStore(reducer,
applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(rootSaga)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.body.appendChild(document.createElement('div')))Copy the code
saga.js
import { delay, put, takeEvery, all } from 'redux-saga/effects'
function* helloSaga() {
console.log('Hello saga! ')}function* incrementAsync() {
/ / delay 1 s
yield delay(1000)
yield put({
type: 'increment'})}function* watchIncrementAsync() {
yield takeEvery('incrementAsync', incrementAsync)
}
export default function* rootSaga() {
yield all([
helloSaga(),
watchIncrementAsync()
])
}
Copy the code
Github source code reference: github.com/kexinWeb/re…