【问题标题】:Redux: Waiting for state changes before doing something elseRedux:在做其他事情之前等待状态变化
【发布时间】:2018-08-27 17:15:23
【问题描述】:

在一个假设的应用中,用户可以创建一本书,默认情况下该书有 3 页。添加书籍和页面后,我想对书籍和页面做一些其他的事情。 Books 和 Pages 是我的 redux 存储中的两个单独的 reducer,也是我的数据库中的两个表。我目前的流程是这样的:

  1. 用户创建新书。
  2. 已调用 ADD_BOOK_REQUEST 操作
  3. 调用了 ADD_PAGES_REQUEST 操作
  4. 图书已添加到触发 ADD_BOOK_SUCCESS 的数据库中
  5. 将页面添加到触发 ADD_PAGES_SUCCESS 的数据库

如上所述,在执行第 4 步和第 5 步之后,我想对这本书做一些其他的事情,但我不确定最好的方法。只是为了让它现在运行,目前我有一个 setTimeout 函数运行检查新书和具有该 bookId 的 3 页。如果书本和 3 页在商店中,请执行其他操作,如果没有,请再次运行超时。我很确定这不是最好的方法。 :微笑:

【问题讨论】:

  • 你应该看看 Redux Sagas。它们使异步流更易于管理。有点绕着你的头。你也可以只使用带有 async/await 的 redux-thunks。

标签: node.js reactjs redux


【解决方案1】:

Gosha Arinich 在他的帖子 Detecting state changes with Redux-Saga 中描述了一种可能的解决方案。

在那篇文章中,他建议编写一个 waitFor 函数,该函数将在提供的选择器最终返回 true 时返回:

function* waitFor(selector) {
  if (yield select(selector)) return; // (1)

  while (true) {
    yield take('*'); // (1a)
    if (yield select(selector)) return; // (1b)
  }
}

可以这样使用:

function* someSaga() {
  yield call(waitFor, state => getCurrentUser(state) != null);
  // the user is logged in by now
}

另一种可能的解决方案是更简单的自定义中间件,它会在分派操作后进行检查。

【讨论】:

    【解决方案2】:

    你有几个选择。

    1) 在您的 React 组件中,您可以连接到在 ADD_PAGES_SUCCESS 时由 reducer 更新的状态。在componentWillReceiveProps 中,您可以检查该连接的状态,并在您的组件中分派您希望在步骤 (5) 之后完成的操作。

    ...不太好,因为componentWillReceiveProps 会很快变得毛茸茸,应该尽可能避免。

    2) 使用 redux-thunk 或 redux-saga:

    使用 Redux-saga,在您的 ACTION 处理程序 (sagas) 中,您可以 take ACTIONS 并执行诸如有条件地进行 API 调用和 yield(等待)服务器响应等操作,此时您可以触发 (@ 987654325@) 更多操作或进行更多 API 调用或做任何你想做的事情。这不会替换你的 reducer,也不会改变你的 redux 状态。

    例子:

    // worker Saga: will be fired on ADD_PAGES_SUCCESS actions
    
    function* doStuffAfterAddPages(action) {
      while (true) {
        yield take(ADD_PAGES_SUCCESS);
    
        try {
          let moreData;
          const result = yield call(api.doSomethingElse);
    
          if (result.isExpected) {
            moreData = yield call(api.doOtherStuff);
          } else {
            moreData = yield call(api.doOtherStuff);
          }
    
          yield put(OTHER_STUFF_COMPLETE, { data: moreData });
        } catch (error) {
          yield put(OTHER_STUFF_FAILED, { error: error.message);
        }
      }
    }
    

    【讨论】:

    • 啊,是的,这是有道理的。我已经在使用 redux-thunk 来更新数据库,所以我想我会坚持下去。现在我突然想到,我可以有一个 Books 和 Pages 减速器都响应的动作。如果动作创建者是addBookWithPages,它在哪里?我有 actions/books.js 和 actions/pages.js 它是住在其中之一还是应该住在其他地方? (我知道 redux 并不在乎,但我会尽可能地学习最佳实践)
    • 实际上,在仔细考虑这一点时,我仍然不确定 React 是如何知道在调用 OTHER_STUFF_COMPLETE 操作时更新 UI 的。例如,假设在创建书籍和页面后,我想在 UI 中加载书籍。唯一想到的是创建一个新的状态,类似于bookWithPagesReady,它有一个ID。当bookWithPagesReady 更新时,我的 UI 会看到它并重定向用户,然后清除该 ID。然而,这似乎很麻烦。
    • 我想说,通过您的设置,动作可以存在于 books.js 中。或者您可以将这两个文件组合起来并称它们为“books.js”,因为书籍包含页面。关于 bookWithPagesReady.. 你也许可以这样做。或者您可以查找您可能通过 redux 与您的组件连接到的 redux 状态页面的数量。您可以在 componentWillReceiveProps 中将旧道具与新道具进行比较。如果新道具有页面而旧道具没有,那么是时候采取新的行动了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-30
    相关资源
    最近更新 更多