我正在尝试创建一个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
}
}
所以我基本上想要等待anotherPart
更改,因为最初它会为false,并且只在循环中执行一次(即使listenToChange
多次执行。这是可能的吗? ?
答案 0 :(得分:1)
正如亚历克斯在评论中提到的那样,倾听状态变化归结为听取可能引发这一状态改变的行为。
take
效果可以采用各种模式描述操作作为参数,这可以帮助您做到这一点:动作,动作数组,函数等。如果您不想白名单这样的操作,您甚至可以在没有参数的情况下调用take
(如果您想要更明确,则使用字符串'*'
),这使您有机会在每次操作后检查状态。
有了这个想法,等待一个状态具有给定值的传奇可以这样写:
function *waitForStateToHaveValue(selector, expectedValue) {
let stateSlice = yield select(selector);
while (stateSlice !== expectedValue) {
yield take();
stateSlice = yield select(selector);
}
}
答案 1 :(得分:0)
我采用了以下模式,该模式正是您所描述的。
它通过等待通过商店的每个动作来工作,并重复选择器以查看特定值是否已更改,从而触发传奇。
签名是一个包装函数,使您可以传递选择器和传奇。传奇必须接受上一个和下一个值。对于所选值的每次更改,包装函数都会“移交给”您的传奇。您应该在传奇中编写逻辑,以便在满足相关条件时使用正常的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
}
}
}
下面是一个经过测试的示例,它在我的应用程序中定义了一个传奇。它会产生正常的传奇,以正常的方式运行。
只要状态的“ focusId”值发生更改,逻辑就会运行。我的sagas进行与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)
}
}
}
})
}
通过与@alex和@vonD进行对比,我个人很舒服地监视状态,并且我觉得它可以充分发挥作用,并且提供了一种简洁而可靠的方式,可以在没有不必要的间接访问的情况下不丢失您关心的更改。如果仅跟踪动作,则很容易通过创建可更改状态的动作来引入错误,而不必记住将动作类型添加到过滤器中。但是,如果您认为重复选择器的性能是一个问题,则可以缩小“采取”的过滤条件,以便仅响应您知道会影响要监视的状态树部分的某些操作。>
UPDATE
以@vonD所示的方法为基础,我以一种更简洁的方式重构了上面的示例。 monitorSelector()函数与常规的基于收益的传奇故事流进行交互,而无需包装任何内容。它为传奇提供了一种“阻止”等待值更改的方式。
function* monitorSelector(selector, previousValue, takePattern = "*") {
while (true) {
const nextValue = yield select(selector)
if (nextValue !== previousValue) {
return nextValue
}
yield take(takePattern)
}
}
这是原始示例中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
}
}