【问题标题】:Listening to store change in redux saga在 redux saga 中监听 store 变化
【发布时间】:2017-08-21 17:17:36
【问题描述】:

我正在尝试创建一个 redux 传奇,它将监听状态中一个变量的变化。当它确实发生变化时,我想调度其他一些操作。这可能吗?

这就是我想做的:

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 改变,因为最初它会是假的,并且只在循环中执行一次(即使listenToChange 被执行多次。这可能吗?

【问题讨论】:

  • 理论上有不同的方法可以做到这一点,但我不认为在 sagas 中听变化的状态是惯用的。您没有负责在减速器中触发anotherPartOfState 更改的操作吗?然后你可以takeLatest这个特定的动作。

标签: reactjs redux redux-saga


【解决方案1】:

正如 Alex 在他的评论中提到的,监听状态变化归结为监听可能触发该状态变化的动作。

take 效果可以将各种描述动作的模式作为参数,这可以帮助您做到这一点:一个动作、一个动作数组、一个函数等。如果您不想将这些动作列入白名单,您甚至可以在不带参数的情况下调用take(如果您想更明确一点,也可以使用字符串'*'),这样您就有机会在每次操作后检查状态。

考虑到这一点,等待某个状态具有给定值的 saga 可以这样写:

function *waitForStateToHaveValue(selector, expectedValue) {
  let stateSlice = yield select(selector);
  while (stateSlice !== expectedValue) {
    yield take();
    stateSlice = yield select(selector);
  }
}

【讨论】:

    【解决方案2】:

    我采用了下面的模式,它完全符合您的描述。

    它的工作原理是等待通过 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
        }
      }
    }
    

    下面是一个经过测试的示例,它在我的应用程序中定义了一个 saga。它生成一个正常的 saga,以正常的方式运行。

    只要状态的“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 相比,我个人对监控状态感到满意,我觉得它执行得很好,并提供了一种简洁可靠的方法,不会错过您关心的更改,而无需不必要的间接。如果您只跟踪操作,很容易通过创建更改状态的操作来引入错误,而不记得将操作类型添加到您的过滤器。但是,如果您认为重复选择器的性能是一个问题,您可以缩小“采取”的过滤器,以便仅响应您知道会影响您正在监视的状态树部分的某些操作。

    更新

    在@vonD 展示的方法的基础上,我以一种更简洁的方式重构了上面的示例。 monitorSelector() 函数与传统的基于产量的 saga 流进行交互,而无需包装任何内容。它为 saga 提供了一种“阻塞”以等待更改值的方法。

    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
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-28
      • 2017-04-19
      • 2021-03-17
      • 1970-01-01
      • 2021-07-13
      • 2020-11-06
      • 1970-01-01
      • 2013-07-22
      相关资源
      最近更新 更多