use*_*999 4 reactjs redux redux-saga
我正在尝试创建一个 redux saga 来监听状态中一个变量的变化。当它确实发生变化时,我想发送一些其他动作。这可能吗?
这就是我想要做的:
yield takeLatest(fooAction, fetchAll);
function* fetchAll() {
const part = yield select(getPartOfState);
if (part.flag) {
yield call(listenToChange);
}
}
function* listenToChange() {
const anotherPart = yield select(getAnotherPartOfState);
if (anotherPart === true) { // this is what I want to wait for
// do something
}
}
Run Code Online (Sandbox Code Playgroud)
所以我基本上想等待anotherPart改变,因为最初它会是假的,并且只在循环中执行一次(即使listenToChange被执行多次。这可能吗?
我采用了下面的模式,它完全符合你的描述。
它的工作原理是等待通过 store 的每个动作,并重复选择器以查看特定值是否已更改,从而触发 saga。
签名是一个包装函数,它使您能够传递一个选择器和一个 saga。传奇必须接受上一个和下一个值。对于所选值的每次更改,包装函数都会“移交”到您的 saga 一次。当满足相关条件时,您应该在您的 saga 中编写逻辑以使用正常的 yield 调用从包装生成器“接管”。
import { take, spawn, select } from "redux-saga/effects"
function* selectorChangeSaga(selector, saga) {
let previous = yield select(selector)
while (true) {
const action = yield take()
const next = yield select(selector)
if (next !== previous) {
yield* saga(next, previous)
previous = next
}
}
}
Run Code Online (Sandbox Code Playgroud)
下面是一个经过测试的示例,它在我的应用程序中定义了一个 saga。它生成一个正常的传奇,以正常的方式运行。
只要状态的“focusId”值发生变化,逻辑就会运行。我的传奇执行与 id 对应的远程数据的延迟加载,并随机刷新服务器列表。注意星号,尤其是yield * delegating yield!它定义了生成器如何相互“切换”。
//load row when non-null id comes into focus
function* focusIdSaga() {
yield* selectorChangeSaga(state => state.focusId, function* (focusId, prevFocusId) {
const { focusType, rows } = yield select()
if (focusType) {
if (!prevFocusId) { //focusId previously new row (null id)
//ensure id list is refreshed to include saved row
yield spawn(loadIdsSaga, focusType)
}
if (focusId) { //newly focused row
if (!rows[focusId]) {
//ensure it's loaded
yield spawn(loadRowSaga, focusType, focusId)
}
}
}
})
}
Run Code Online (Sandbox Code Playgroud)
与@alex 和@vonD 相比,我个人对监控状态感到很舒服,我觉得它表现得足够好,并提供了一种简洁可靠的方式,不会错过您关心的更改,而无需进行不必要的间接操作。如果您只跟踪操作,则很容易通过创建更改状态的操作来引入错误,同时又不记得将操作类型添加到您的过滤器中。但是,如果您认为重复选择器的性能是一个问题,您可以缩小“采取”的过滤器的范围,以便仅响应您知道会影响您正在监视的状态树部分的某些操作。
更新
基于@vonD 所示的方法,我以更简洁的方式重构了上面的示例。monitorSelector() 函数与传统的基于产量的 saga 流程交互,无需包装任何东西。它为 saga 提供了一种“阻止”以等待更改值的方法。
function* monitorSelector(selector, previousValue, takePattern = "*") {
while (true) {
const nextValue = yield select(selector)
if (nextValue !== previousValue) {
return nextValue
}
yield take(takePattern)
}
}
Run Code Online (Sandbox Code Playgroud)
这是原始示例中 saga 的经过测试的版本,但针对监控状态的新方式进行了重构。
//load row when non-null id comes into focus
function* focusIdSaga() {
let previousFocusId
while (true) {
const focusId = yield* monitorSelector(state => state.focusId, previousFocusId)
const { focusType, rows } = yield select()
if (focusType) {
if (!previousFocusId) { //focusId previously new row (null id)
//ensure id list is refreshed to include saved row
yield spawn(loadIdsSaga, focusType)
}
if (focusId) { //newly focused row
if (!rows[focusId]) {
//ensure it's loaded
yield spawn(loadRowSaga, focusType, focusId)
}
}
}
previousFocusId = focusId
}
}
Run Code Online (Sandbox Code Playgroud)
正如亚历克斯在他的评论中提到的,监听状态变化归结为监听可能触发该状态变化的操作。
该take效果可以采用描述操作的各种模式作为参数,这可以帮助您做到这一点:一个操作、一组操作、一个函数等。如果您不想将此类操作列入白名单,您甚至可以不take调用一个参数(或者'*'如果你想更明确的话可以使用字符串),这让你有机会在每个操作之后检查状态。
考虑到这一点,等待一个状态具有给定值的传奇可以这样写:
function *waitForStateToHaveValue(selector, expectedValue) {
let stateSlice = yield select(selector);
while (stateSlice !== expectedValue) {
yield take();
stateSlice = yield select(selector);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4615 次 |
| 最近记录: |