redux-saga
是一个用于管理应用程序副作用(例如数据获取、访问浏览器缓存、设备API的调用等)的库。它的主要功能是使 redux 应用程序的副作用更容易管理、执行、测试和调试。
以下是 redux-saga
的主要概念和功能:
Generators:
redux-saga
使用 ES6 的 Generator 函数来执行 sagas(这是一个核心概念)。Generators 允许函数暂停并恢复执行,这对于管理异步代码非常有用。
Effects:
Effects 是一个 JavaScript 对象,用于描述某些控制流指令。例如,call
是一个 effect,用于调用异步函数。另一个常用的 effect 是 put
,用于分发一个 action。
Watcher Sagas:
这些 sagas "监听"特定的 action,并根据这些 action 触发其他 sagas。例如,您可能有一个 watcher saga,当检测到 FETCH_DATA_REQUEST
action 时,它会启动一个 saga 来获取数据。
Worker Sagas: 当 watcher sagas 捕获到一个特定的 action 时,worker sagas 会被调用执行实际的异步操作。
Blocking vs Non-blocking Calls:
使用 yield call(fn, ...args)
是阻塞调用,saga 会等待函数执行完毕。而使用 yield fork(fn, ...args)
则是非阻塞调用,saga 会继续执行下一个指令。
Error Handling:
因为 sagas 使用 JavaScript 生成器,所以你可以使用普通的 try/catch
语句来处理错误。
Cancellation: Sagas 可以被取消。例如,如果用户在数据加载时切换了页面,您可能想取消正在进行的数据获取操作。
Parallel Execution:
使用 yield all([saga1(), saga2(), ...])
可以并行执行多个 sagas。
总之,redux-saga
提供了一种优雅的方式来处理复杂的副作用管理。与 redux 的 thunk
或 promise
中间件相比,它提供了更多的功能和更大的灵活性。
npm install redux redux-saga react-redux --save
take
put
是 redux-saga
中的一个 Effect 创建器。在 redux-saga
的上下文中,Effect 可以被认为是一个描述了某种副作用(如异步操作、事件订阅等)的对象,但它自身并不执行这些副作用。这允许我们编写纯函数式的、可测试的代码。
put
用于创建一个 Effect,描述了向 Redux store 发出一个 action 的过程。这与 dispatch
函数在纯 Redux 中的角色相似。
使用方式
在一个 redux-saga
的 generator 函数中,可以如下使用 put
:
import { put } from 'redux-saga/effects';
function* someSaga() {
// ... some logic
yield put({ type: 'SOME_ACTION', payload: 'some data' });
// ... more logic
}
在上述例子中,我们在 saga 中使用了 put
Effect 来发出一个 SOME_ACTION
的 action。当 saga 到达这一行时,这个 action 会被发往 Redux store,就好像我们在传统的 Redux 中调用 dispatch({ type: 'SOME_ACTION', payload: 'some data' })
一样。
工作原理
当 saga interpreter(saga 的运行时)碰到一个 put
Effect 时,它会知道要将对应的 action 对象发送到 Redux store。这一行为是由 middleware(saga middleware)完成的,因此你不需要直接与 Redux store 互动。
注意点
put
只是创建了一个 Effect,描述了要做的事情,但它并不执行实际的动作。实际的动作是由 saga middleware 执行的。put
在非 generator 函数中不起作用,因为它只是返回一个纯对象,而不是真的发出 action。总的来说,put
Effect 提供了一种纯函数式、可测试的方式来描述向 Redux store 发出 action 的过程,从而使得我们能够在 sagas 中处理复杂的逻辑和异步操作。
put
redux-saga
是一个用于管理应用程序 Side Effects(例如:异步获取数据、访问浏览器缓存等)的库。它的目标是使副作用管理更加简单和高效,同时使测试变得容易。
在 redux-saga
中,put
是一个非常重要的 effect,用于触发 action。与 Redux 中的 dispatch
方法类似,但它的工作方式有些不同。
put的基本介绍
定义:put
是一个 effect 创建器,它返回一个纯对象描述应该发生的 Side Effect。具体来说,它会告诉 redux-saga
middleware 需要分发一个 action 到 Redux store。
返回值:put
返回一个 Effect 描述对象,该对象将被 redux-saga
middleware 解释并执行相应的副作用。
使用方式:
import { put } from 'redux-saga/effects';
function* someSaga() {
// 使用 put 发出 action
yield put({ type: 'ACTION_TYPE', payload: {} });
}
为什么使用 put
而不是 dispatch
?
虽然在功能上看起来类似于 dispatch
,但 put
和 dispatch
有一些关键的区别:
声明性:put
创建的是一个 Effect 描述对象,而不是直接触发 action。这允许我们编写纯净、可测试的 generator 函数,因为它们不直接产生副作用。
顺序执行:当你在 saga 中 yield
一个 put
effect,saga 会等待这个 action 完全处理完成(即所有相应的 reducers 和 sagas 完全处理完成后)再继续执行后面的代码。
测试:由于 put
只是返回一个描述对象,所以在测试中,你可以轻松地检查这些描述对象,而不必担心它们的执行情况。
结论
在 redux-saga
中,put
提供了一种声明性的方式来分发 actions。它使我们能够在一个统一的上下文中处理异步流程和 Redux actions,而不必担心异步操作和状态更新之间的时序问题。
src/index.js
// 引入 React 核心库
import React from 'react'
// 引入 ReactDOM 客户端渲染库
import ReactDOM from 'react-dom/client';
// 引入 Counter 组件
import Counter from './components/Counter';
// 引入 react-redux 的 Provider 组件
import { Provider } from 'react-redux';
// 引入 Redux store
import store from './store';
// 使用 ReactDOM 的新的 Concurrent 模式渲染方法,将 App 组件渲染到页面的 #root 元素上
ReactDOM.createRoot(document.querySelector('#root')).render(
// 使用 Provider 组件将 Redux store 传递给应用的所有子组件
<Provider store={store}>
<Counter />
</Provider>
)
src\store\rootSaga.js
// 从 redux-saga/effects 中引入 put 和 take 方法
import {put,take} from 'redux-saga/effects';
// 引入 action 类型常量
import * as actionTypes from './action-types';
// 定义一个延迟函数,返回一个延迟指定毫秒数的 Promise
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
// 定义一个工作 Saga,用于执行异步任务
function * workerSaga(){
// 使用延迟函数等待1秒
yield delay(1000);
// 执行 put effect,派发一个 ADD 的 action
yield put({type:actionTypes.ADD});
}
// 定义一个观察者 Saga,用于监听特定的 action,并触发对应的工作 Saga
function * watcherSaga(){
// 使用 take effect 监听 ASYNC_ADD 类型的 action
yield take(actionTypes.ASYNC_ADD);
// 当上面的 action 被捕获时,执行 workerSaga
yield workerSaga();
}
// 导出一个根 Saga,用于在 store 中运行
export default function* rootSaga() {
// 执行观察者 Saga
yield watcherSaga();
}
src/components/Counter.js
// 从 React 中引入 React
import React from 'react';
// 引入 action 类型常量
import * as actionTypes from '../store/action-types';
// 从 react-redux 中引入 useSelector 和 useDispatch 钩子函数
import { useSelector, useDispatch } from 'react-redux';
// 定义 Counter 组件
function Counter() {
// 使用 useSelector 钩子从 Redux store 中获取 number 值
const number = useSelector(state => state.number);
// 使用 useDispatch 钩子获取 dispatch 函数,用于派发 action
const dispatch = useDispatch();
// 返回组件的 JSX 结构
return (
<div>
// 渲染 number 值
<p>{number}</p>
// 当点击按钮时,派发一个 ASYNC_ADD 的 action
<button onClick={() => dispatch({ type: actionTypes.ASYNC_ADD })}>+</button>
</div>
)
}
// 导出 Counter 组件
export default Counter;
src/store/index.js
// 从redux中引入createStore和applyMiddleware方法,注意createStore使用的是老版本的命名
import {legacy_createStore as createStore, applyMiddleware} from 'redux';
// 引入reducer
import reducer from './reducer';
// 引入redux-saga中的createSagaMiddleware方法
import createSagaMiddleware from 'redux-saga';
// 引入rootSaga
import rootSaga from './rootSaga';
// 创建saga中间件
let sagaMiddleware = createSagaMiddleware();
// 创建并应用saga中间件到store
let store = applyMiddleware(sagaMiddleware)(createStore)(reducer);
// 运行rootSaga
sagaMiddleware.run(rootSaga);
// 将store挂载到window对象上,方便调试
window.store = store;
// 导出store
export default store;
src/store/action-types.js
export const ASYNC_ADD='ASYNC_ADD';
export const ADD='ADD';
src/store/reducer.js
import * as types from './action-types';
export default function reducer(state={number:0},action) {
switch(action.type){
case types.ADD:
return {number: state.number+1};
default:
return state;
}
}
fork
fork
是 redux-saga
提供的一个 Effect 创建器。与其他的 Effects(如 call
, put
, take
等)相似,fork
也用于描述在 Redux Saga 任务中需要执行的副作用。不过,与其他 effect 有所不同的是,fork
用于非阻塞调用。
让我们深入了解一下 fork
的特性和如何使用它:
非阻塞调用
fork
effect,我们启动了一个新的 saga 任务,该任务在后台运行。fork
一个任务时,该任务会立即启动,但父 saga 不会等待这个 forked 任务完成,它会继续执行下一行代码。使用方式
import { fork } from 'redux-saga/effects';
function* backgroundTask() {
// 这里的代码是后台任务的代码
}
function* mySaga() {
yield fork(backgroundTask);
// 当我们达到这一行时,backgroundTask 已经在后台启动,并且 mySaga 不会等待它完成。
}
3. 错误处理
fork
的任务中发生了错误,并且该错误没有被捕获,那么该错误将冒泡到父 saga。如果在父 saga 中也没有捕获这个错误,那么它将冒泡到 saga middleware,并导致整个 saga 中止。4. 取消任务
fork
返回一个 Task 对象,我们可以使用这个对象来取消该任务。fork
相关的另一个 effect 是 cancel
,它可以用于取消一个正在运行的 forked 任务。import { fork, cancel, take } from 'redux-saga/effects';
function* backgroundTask() {
// 这里的代码是后台任务的代码
}
function* mySaga() {
const task = yield fork(backgroundTask);
// 等待一个特定的 action
yield take('STOP_BACKGROUND_TASK');
// 然后取消任务
yield cancel(task);
}
结论
在 redux-saga
中,fork
提供了一种方法来启动并行的非阻塞任务。这在处理后台任务或其他与 UI 更新无关的任务时非常有用。而与 fork
一起使用的 cancel
effect 则允许我们在需要时取消这些任务。
+import {put,take,fork} from 'redux-saga/effects';
import * as actionTypes from './action-types';
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function * workerSaga(){
yield delay(1000);
yield put({type:actionTypes.ADD});
}
function * watcherSaga(){
yield take(actionTypes.ASYNC_ADD);
+ yield fork(workerSaga);
}
export default function* rootSaga() {
yield watcherSaga();
}
takeEvery
takeEvery
是 redux-saga
中的一个高级 Effect 创建器,用于监听传入的特定类型的 action,并为每个接收到的 action 执行给定的 saga。
让我们详细了解 takeEvery
的工作原理和如何使用它:
1. 基本工作原理
takeEvery
监听特定类型的 action。2. 使用方式
import { takeEvery } from 'redux-saga/effects';
function* handleSomeAction(action) {
// 处理 action 的逻辑
}
function* mySaga() {
yield takeEvery('SOME_ACTION_TYPE', handleSomeAction);
}
在上面的例子中,每当一个类型为 'SOME_ACTION_TYPE'
的 action 被派发时,handleSomeAction
saga 将被启动,并接收该 action 作为其参数。
3. 非阻塞
takeEvery
是非阻塞的,这意味着在 saga 中执行 yield takeEvery(...)
后,saga 将继续执行后面的代码而不等待。4. 实现细节
如果你想知道 takeEvery
的背后实现,实际上它是使用 take
和 fork
实现的。以下是一个简化的版本:
function* takeEvery(actionType, saga) {
while (true) {
const action = yield take(actionType);
yield fork(saga, action);
}
}
5. 注意事项
takeEvery
为每个接收到的 action 都启动了一个新的 saga 实例,如果这些 action 非常频繁,并且处理它们的 saga 执行时间很长,可能会导致大量并行的 saga 任务。这在某些情况下可能不是你想要的,因此在使用时需要注意。takeLatest
而不是 takeEvery
。结论
takeEvery
是 redux-saga
提供的一个非常有用的工具,用于响应并处理特定类型的 action。在许多常见的应用场景中,它都很有用,但在使用时需要考虑其非阻塞性质和可能产生的并行任务。
+import {put,takeEvery} from 'redux-saga/effects';
import * as actionTypes from './action-types';
export function* add() {
yield put({ type: actionTypes.ADD });
}
export default function* rootSaga() {
+ yield takeEvery(actionTypes.ASYNC_ADD,add);
}
call
call
是 redux-saga
的一个 Effect 创建器,它用于调用函数或方法,并返回一个纯对象(Effect 描述对象),而不是直接执行它。这使得我们的 sagas 更容易测试,因为我们可以检查这些 Effect 描述对象,而不必实际执行任何副作用。
下面详细了解 call
:
1. 基本工作原理
call
可以保证 redux-saga 中的函数调用是阻塞的,这意味着 saga 将等待该函数调用完成并返回结果后再继续执行。2. 使用方式
调用普通函数
import { call } from 'redux-saga/effects';
function fetchData() {
return fetch('/api/data').then(res => res.json());
}
function* mySaga() {
const data = yield call(fetchData);
// 一旦 fetchData 完成,以下代码将会执行
console.log(data);
}
调用对象方法:
const api = {
fetchData() {
return fetch('/api/data').then(res => res.json());
}
};
function* mySaga() {
const data = yield call([api, api.fetchData]);
console.log(data);
}
传递参数:
function fetchItem(id) {
return fetch(`/api/data/${id}`).then(res => res.json());
}
function* mySaga() {
const id = 1;
const item = yield call(fetchItem, id);
console.log(item);
}
3. 为什么使用 call
?
使用 call
的主要好处是它使得你的 saga 更容易进行单元测试。由于 call
只是返回一个 Effect 描述对象而不是直接执行函数,我们可以轻松地断言该对象,而不必实际运行任何异步逻辑。
4. 注意事项
call
用于确保函数调用是阻塞的。这意味着,直到该函数完成并返回一个结果,saga 才会继续执行下一行代码。fork
Effect,它会在背景中非阻塞地运行函数。结论
call
是 redux-saga
中的一个核心 Effect,它允许我们在 saga 中进行阻塞式的函数调用。这不仅使我们的异步流程更容易理解,而且使我们的 sagas 更容易测试。
src\store\rootSaga.js
// 从redux-saga中引入put,takeEvery和call效果
import {put, takeEvery, call} from 'redux-saga/effects';
// 引入action类型常量
import * as actionTypes from './action-types';
// 定义一个延迟函数
const delay = ms => new Promise((resolve) => {
// 使用setTimeout进行延迟
setTimeout(() => {
resolve();
}, ms);
});
// 定义一个saga生成器函数,用于处理异步加法
export function* add() {
// 使用call效果来调用delay函数实现延迟
yield call(delay, 1000);
// 使用put效果派发ADD action
yield put({ type: actionTypes.ADD });
}
// 定义rootSaga生成器函数
export default function* rootSaga() {
// 使用takeEvery监听每一个ASYNC_ADD action,并触发add saga处理
yield takeEvery(actionTypes.ASYNC_ADD, add);
}
cps
在 redux-saga
中,cps
是一个 Effect 创建器,用于处理 Node 风格的回调函数(即采用 (error, result) => ...
的回调函数)。在 Node.js 生态中,许多旧的库和某些核心模块使用这种风格的回调。
当我们需要在 saga 中调用这类函数时,cps
变得很有用。
基本工作原理
cps
可以确保 Node 风格的回调函数在 saga 中是阻塞的。这意味着,直到该函数调用完成并回调被执行,saga 才会继续执行下一行代码。error
)是非 null
/undefined
,saga 会自动抛出一个错误。使用方式
考虑以下一个简单的 Node 风格的读文件函数:
const fs = require('fs');
function readFile(path, cb) {
fs.readFile(path, 'utf-8', cb);
}
在 saga 中,我们可以使用 cps
如下:
import { cps } from 'redux-saga/effects';
function* readSaga() {
try {
const content = yield cps(readFile, '/path/to/file.txt');
console.log(content);
} catch (err) {
console.error('File read failed:', err);
}
}
为什么使用 cps
?
当你在 saga 中与 Node 风格的回调 API 进行交互时,cps
提供了一个简洁的方式来进行异步操作,而不必将回调函数转换为 Promise。
它使得你的 saga 代码保持一致性,即使底层的 API 使用回调而不是 Promise。
它还为你处理了错误处理,自动捕获回调中的错误,并使你可以在 try/catch
块中捕获它们。
注意事项
使用 cps
调用的函数必须采用 Node 风格的回调。
与其他 Effect 创建器类似,cps
也只是返回一个 Effect 描述对象,而不是真正执行函数。实际的执行是由 saga 中间件负责的。
结论
cps
是 redux-saga
中处理 Node 风格回调函数的特定 Effect。当与此类回调 API 进行交互时,它为 saga 提供了一个简洁、一致的方式。
src\store\rootSaga.js
// 从redux-saga中引入put,takeEvery,call和cps效果
import {put, takeEvery, call, cps} from 'redux-saga/effects';
// 引入action类型常量
import * as actionTypes from './action-types';
// 定义一个带回调的延迟函数
const delay = (ms, callback) => {
// 使用setTimeout进行延迟,并在完成后调用回调函数
setTimeout(() => {
callback(null, 'ok');
}, ms);
}
// 定义一个saga生成器函数,用于处理异步加法
export function* add() {
// 使用cps效果调用带回调的delay函数并获取结果
let data = yield cps(delay, 1000);
// 打印cps返回的数据
console.log(data);
// 使用put效果派发ADD action
yield put({ type: actionTypes.ADD });
}
// 定义rootSaga生成器函数
export default function* rootSaga() {
// 使用takeEvery监听每一个ASYNC_ADD action,并触发add saga处理
yield takeEvery(actionTypes.ASYNC_ADD, add);
}
all
all
是 redux-saga
的一个 Effect 创建器。它的主要作用是并行执行多个 Effects,并等待它们全部完成。这是一个非常有用的工具,尤其是当你想同时启动多个任务,并等待所有任务都完成时。
以下是 all
的主要特点和使用方法:
1. 基本工作原理
all
接受一个数组作为参数,这个数组包含了你想并行执行的 Effects。all
的 Effects 都完成时,all
会返回一个包含所有 Effects 返回值的数组。2. 使用方式
假设我们有两个可以并行执行的异步任务,我们可以使用 all
来同时执行它们:
import { all, call } from 'redux-saga/effects';
function* fetchUsers() {
const users = yield call(fetchAPI, '/users');
// ... 处理 users
}
function* fetchProducts() {
const products = yield call(fetchAPI, '/products');
// ... 处理 products
}
function* mySaga() {
const [users, products] = yield all([fetchUsers(), fetchProducts()]);
// 这里,users 和 products 都已经准备好了
}
在上面的示例中,fetchUsers
和 fetchProducts
任务会并行执行。只有当这两个任务都完成时,mySaga
才会继续执行。
3. 错误处理
如果传递给 all
的任何 Effect 失败(例如,一个 Promise 被拒绝),则 all
也会立即失败,并拒绝首个失败的 Effect 的错误。此时,其他尚未完成的 Effects 会继续执行,但它们的结果将被忽略。
4. 为什么使用 all
?
并行性:在某些场景下,你可能不希望按顺序一个接一个地执行异步操作,而是希望并行执行它们以提高效率。all
允许你做到这一点。
代码简洁性:all
提供了一个清晰的方式来组织和运行并行的 Effects。
结论
all
是 redux-saga
提供的一个强大的 Effect 创建器,用于并行执行多个 Effects 并等待它们全部完成。当需要并行处理多个任务时,它是非常有用的。
src\store\rootSaga.js
// 从redux-saga中引入所需的效果
import { put, take , call, cps, all } from 'redux-saga/effects';
// 引入action类型常量
import * as actionTypes from './action-types';
// 定义一个saga生成器函数,用于处理一次异步加法
export function* add1() {
// 循环一次
for (let i = 0; i < 1; i++) {
// 等待一个ASYNC_ADD action
yield take(actionTypes.ASYNC_ADD);
// 派发一个ADD action
yield put({ type: actionTypes.ADD });
}
// 打印完成的消息
console.log('add1 done ');
// 返回结果
return 'add1Result';
}
// 定义另一个saga生成器函数,用于处理两次异步加法
export function* add2() {
// 循环两次
for (let i = 0; i < 2; i++) {
// 等待一个ASYNC_ADD action
yield take(actionTypes.ASYNC_ADD);
// 派发一个ADD action
yield put({ type: actionTypes.ADD });
}
// 打印完成的消息
console.log('add2 done ');
// 返回结果
return 'add2Result';
}
// 定义rootSaga生成器函数
export default function* rootSaga() {
// 使用all效果并行执行add1和add2,并等待它们都完成
let result = yield all([add1(), add2()]);
// 打印完成消息和结果
console.log('done', result);
}
cancel
cancel
是 redux-saga
的一个 Effect 创建器,它用于取消正在运行的任务(通常是由 fork
, spawn
, 或 race
所创建的任务)。当任务被取消时,如果该任务正在执行一个阻塞调用(例如 call
或 take
),那么该调用会被中断并抛出一个 SagaCancellationException
。
以下是关于 cancel
的详细说明:
1. 基本工作原理
cancel
可以用来停止正在运行的 fork
或 spawn
任务。2. 使用方式
假设有一个 backgroundTask
函数,我们希望在特定的 action 被触发时取消这个任务:
import { fork, take, cancel } from 'redux-saga/effects';
function* backgroundTask() {
// ... 执行一些操作
}
function* watchStartBackgroundTask() {
const bgTask = yield fork(backgroundTask);
yield take('STOP_BACKGROUND_TASK');
yield cancel(bgTask);
}
在上述代码中,当 watchStartBackgroundTask
被触发时,backgroundTask
会作为一个后台任务启动。当 STOP_BACKGROUND_TASK
action 被触发时,我们使用 cancel
Effect 来取消 backgroundTask
。
3. 为什么使用 cancel
?
控制并发任务:在某些场景下,我们可能希望确保一次只有一个特定的任务在运行。例如,如果用户点击一个加载数据的按钮多次,我们可能只希望执行最新的请求,而取消前一个。cancel
可以帮助我们实现这一点。
资源清理:在任务被取消时,可能需要进行一些清理工作,例如清除定时器、取消网络请求等。你可以在 saga 中使用 try/catch/finally
块来处理取消逻辑:
function* cancellableTask() {
try {
// 执行任务
} catch (e) {
if (e instanceof SagaCancellationException) {
// 处理取消逻辑,例如资源清理
} else {
throw e;
}
}
}
4. 注意事项
cancel
仅仅是发出一个取消任务的指令,它并不会立即停止任务。任务中当前的阻塞 Effect(如 call
或 take
)将被中断,并抛出 SagaCancellationException
。
要处理可能的取消,你可以使用 try/catch
结构,如上述示例所示。
结论
cancel
是 redux-saga
的一个重要 Effect,它为我们提供了在特定条件下终止正在运行的任务的能力。当结合 fork
, spawn
, 和 race
使用时,cancel
可以帮助我们有效地控制和管理并发操作。
src\store\rootSaga.js
// 从redux-saga中引入所需的效果
import { put, take, cancel, fork, delay } from 'redux-saga/effects';
// 引入action类型常量
import * as actionTypes from './action-types';
// 定义一个saga生成器函数,持续地每隔1秒发起ADD action
export function* add() {
// 无限循环
while (true) {
// 延迟1秒
yield delay(1000);
// 派发一个ADD action
yield put({
type: actionTypes.ADD
});
}
}
// 定义监视器saga生成器函数,它负责fork出上面的add saga并在需要时取消它
export function* addWatcher() {
// 使用fork开启一个新的add saga任务
const task = yield fork(add);
// 打印fork返回的任务对象
console.log(task);
// 等待一个STOP_ADD action
yield take(actionTypes.STOP_ADD);
// 取消add saga任务
yield cancel(task);
}
// 定义请求saga生成器函数,用于处理网络请求
function* request(action) {
// 获取请求的url
let url = action.payload;
// 创建一个网络请求的promise
let promise = fetch(url).then(res => res.json());
// 等待promise完成并获取响应数据
let res = yield promise;
// 打印响应数据
console.log(res);
}
// 定义监视器saga生成器函数,它负责fork出上面的request saga并在需要时取消它
function* requestWatcher() {
// 等待一个REQUEST action
const requestAction = yield take(actionTypes.REQUEST);
// 使用fork开启一个新的request saga任务并传入action
const requestTask = yield fork(request, requestAction);
// 等待一个STOP_REQUEST action
yield take(actionTypes.STOP_REQUEST);
// 取消request saga任务
yield cancel(requestTask);
}
// 定义rootSaga生成器函数
export default function* rootSaga() {
// 执行addWatcher saga
yield addWatcher();
// 执行requestWatcher saga
yield requestWatcher();
}
src\store\action-types.js
export const ASYNC_ADD='ASYNC_ADD';
export const ADD='ADD';
+export const STOP_ADD='STOP_ADD';
+export const REQUEST = 'REQUEST';
+export const STOP_REQUEST = 'STOP_REQUEST';
src\components\Counter.js
import React from 'react';
import * as actionTypes from '../store/action-types';
import { useSelector, useDispatch } from 'react-redux';
function Counter() {
const number = useSelector(state => state.number);
const dispatch = useDispatch();
return (
<div>
<p>{number}</p>
+ <button onClick={() => dispatch({ type: actionTypes.ASYNC_ADD })}>+</button>
+ <button onClick={() => dispatch({ type: actionTypes.STOP_ADD })}>stop</button>
+ <button onClick={() => dispatch({ type: actionTypes.REQUEST, payload: '/users.json' })}>request</button>
+ <button onClick={() => dispatch({ type: actionTypes.STOP_REQUEST })}>stopRequest</button>
</div>
)
}
export default Counter;