introduce

  1. Asynchronous operations are better handled in REdux
  2. Redux-saga adds an extra layer to redux’s original data stream, listening for action
  3. When an action is received, a task is sent to maintain state
  4. Saga is created in Generator mode and synchronized by asynchronous method

Normal REDUx process

Join the process after Redux-Saga


use

import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
// Import the saga file
import { rootSaga } from './rootSaga'
// Use the Redux-Saga module's createSagaMiddleware factory function to create a SagaMiddleware.
// Before running rootSaga, we must connect middleware to Store using applyMiddleware. Then use the

const sagaMiddleware = createSagaMiddleware();
const middlewares = [ sagaMiddleware ];

conststore = createStore(rootReducer, applyMiddleware(... middlewares)); sagaMiddleware.run(rootSaga);Copy the code

Redux-saga auxiliary function

Sage provides helper functions that wrap internal methods to derive tasks when specific actions are initiated to the store

takeEvery

import { call, put } from 'redux-saga/effects'
export function* fetchData(action) {
   try {
      const data = yield call(Api.fetchUser, action.payload.url);
      yield put({type: "FETCH_SUCCEEDED", data});
   } catch (error) {
      yield put({type: "FETCH_FAILED", error}); }}function* watchFetchData() {
  yield* takeEvery('FETCH_REQUESTED', fetchData)
}

// There is one use: monitor all initiated actions
yield takeEvery(The '*', fn)
Copy the code

TakeEvery allows multiple fetchData instances to be started at the same time, even though one or more fetchData instances have not been completed before, We can still start a new fetchData task — this means that the fetchData task will be started only when the FETCH_REQUESTED action is called.

takeLatest

function* watchFetchData() {
  yield* takeLatest('FETCH_REQUESTED', fetchData)
}
Copy the code

TakeLatest allows only one fetchData task to be executed at any time, and that task is the last one to be started. If a previous task executed fetchData when it was started again, The previous task is automatically cancelled — you get the result of the last (latest) call to the FETCH_REQUESTED action.


Effects

concept

Sagas are implemented as Generator functions that can yield the saga logic to JS objects, known as effects,

  1. Sagas are implemented using Generator functions
  2. In a Generator function, any expression to the right of yield will be evaluated, and the result will be yield to the caller
  3. Use yield to explain effects (simple objects)
  4. An Effect is a simple object that contains some information for middleware to explain execution. You can think of effects as instructions sent to middleware to perform certain actions (call some asynchronous function, initiate an action to a store, etc.)
// Official example
import { takeEvery } from 'redux-saga/effects'
import Api from './path/to/api'

If there is an action that calls PRODUCTS_REQUESTED, the effect represented by the second argument will be matched
function* watchFetchProducts() {
  yield takeEvery('PRODUCTS_REQUESTED', fetchProducts)
}
// Execute to get data
Api.fetch is called with Generator. In a Generator function, any expression to the right of yield is evaluated and the result is yield to the caller
function* fetchProducts() {
  const products = yield Api.fetch('/products')
  console.log(products)
}

// The second way
import { call } from 'redux-saga/effects'
//call(fn, ... Args. Unlike the previous example, we now do not make an asynchronous call immediately; instead, call
// Create a message describing the result just as in Redux you use the Action creator to create a plain text object describing the action to be executed by the Store.
//call creates a plain text object describing the function call. Redux-saga Middleware ensures that function calls are made and the generator resumes when the response is resolved
function* fetchProducts() {
  const products = yield call(Api.fetch, '/products')
  // ...
}

Copy the code

Send the action to the store

// The Generator gets the return value and calls Dispatch
function* fetchProducts(dispatch)
  const products = yield call(Api.fetch, '/products')
  dispatch({ type: 'PRODUCTS_RECEIVED', products })}import { call, put } from 'redux-saga/effects'
/ /...
function* fetchProducts() {
  const products = yield call(Api.fetch, '/products')
  // Create and yield a Dispatch Effect
  yield put({ type: 'PRODUCTS_RECEIVED', products })
}
Copy the code

Error handling


import Api from './path/to/api'
import { call, put } from 'redux-saga/effects'

function* fetchProducts() {
  try {
    const products = yield call(Api.fetch, '/products')
    yield put({ type: 'PRODUCTS_RECEIVED', products })
  }
  catch(error) {
    yield put({ type: 'PRODUCTS_REQUEST_FAILED', error })
  }
}

Copy the code

Use try/catch to catch saga error messages


Some of the concepts

Triggering an asynchronous Side Effect from within a Saga is always done by yield some declarative Effect. What a Saga does is actually combine all of those effects to achieve the desired control flow. In use, this is done in the form of yield Effects, and Effects include

  1. call: yield call(Generator, Param yields a call and commands middleware to call a function with the param argument At Generator,saga waits for the Generator to execute, receives the return value, and continues to execute. Call is blocked, that is, it waits for the completion of execution before continuing to execute, and returns the normal result after execution.
  2. Take: is blocked, and execution will continue until it (action.type) is heard. This means creating an effect description that tells the Middleware to wait for an action on the Store, and the Generator will pause until it initiates an action that matches it.
  3. Put: Similar to the Dispatch method, triggers an action that commands middleware to initiate an action request to the Store, and is non-blocking
  4. Fork: non-blocking; it does not need to wait for it to finish. Fork returns a task and can be cancelled
  5. Cancel: Cancel the task returned by the fork method
  6. Select: State can be obtained from global state
  7. Saga: is a function registered with *, a function is a saga
  8. Effect: call,put,take… Is the effect
function* watchAndLog() {
  while (true) {
    const action = yield take(The '*')
    const state = yieldSelect ()}} take, which will pause the Generator until a matching action is initiated and watchAndLog is suspended until any action is initiated.Copy the code

Non-blocking call -fork

Forking a task will start in the background and the caller can continue the process without waiting for the forked task to finish. When we need to have concurrent operations, using the Call effect will block the execution of the saga. When using the fork, we need to care about being blocked or wait for the result to return before continuing

const result = yield fork (saga,param)
Copy the code

Perform multiple tasks simultaneously

const [users, repos] = yield [
  call(fetch, '/users'),
  call(fetch, '/repos')]Copy the code

When multiple tasks need to be executed synchronously, yield an array containing effects, and the Generator blocks until all effects have been executed


use

//redux.connect The action that needs to be bound to props
function mapDispatchToProps(dispatch) {
    return {
        getHome: bindActionCreators(getHomeAdData, dispatch)
    }
}
// An action creator
export function getHomeAdData(){
    return {
        type: actionTypes.HOME_AD_DATA,
    }
}
// Listen on action.type and start the following action
export default function* rootSaga () {
    // Use takeEvery to listen for action type in rootSaga
    yield takeEvery('HOME_AD_DATA', getHomeAdData);
    yield takeEvery('GET_LIKE_LIST_DATA', getLikeListData);
}
// Use yield Call Effect to get the return value
export function* getHomeAdData() {
    let data = yield call(getAdData)
    ...
    yield put({type:UPDATE_HOME_AD_DATA, data: dataArr})
}

export function getAdData() {
    const result = axios.get('/api/homead')
    return result
}
Copy the code