在 Redux 中链接多个异步调度

Posted

技术标签:

【中文标题】在 Redux 中链接多个异步调度【英文标题】:chaining multiple async dispatch in Redux 【发布时间】:2016-08-09 11:48:32 【问题描述】:

我正在尝试以以下方式将多个操作链接在一起:

A.将用户数据发布到数据库

B.使用发布的数据查询 Elasticsearch 的结果

(我同时做A和B)

B1。使用 ES 的结果,从两个表中查询原始数据库的结果 B2。导航到新页面并更新 UI

我现在正在使用 thunk 来推理我的代码,但我也发现这种异步模式非常冗长:

export function fetchRecipes(request) 
  return function(dispatch) 
    dispatch(requestRecipes(request))    
    return fetch(url)
      .then(response => response.json())
      .then(json => dispatch(receiveRecipes(request, json))
    )
  

这个,连同“requestRecipes”和“receiveRecipes”作为其他动作创建者似乎只是为了进行一个异步调用。 (请求、接收和获取函数)

总结:当您链接 2-3 个输出相互依赖的异步操作时(我需要在可能的情况下进行承诺),是否有更有效的方法可以在不为每个异步调用编写 3 个函数的情况下这样做?

我想一定有办法。我正在对 Redux 文档进行模式匹配,很快就对我正在创建的函数感到不知所措

非常感谢您的反馈!

【问题讨论】:

我用这个模式;它有很多功能,但您也可以创建一个工厂,在您的应用程序启动时为您创建所有这些功能。例如,请参阅 react-redux-universal-hot-example 中的 this middleware(我从该示例 repo 中的各种模式中找到了很多灵感)。同时,我并不认为它是在异步操作的上下文中使用 redux 的一种真正方式。我很想在这里看到其他回复。 【参考方案1】:

您可以使用redux-saga 而不是redux-thunk 来更轻松地实现此目的。 redux-saga 让您可以使用生成器描述您的工作,并且更容易推理。

第一步是描述如何将数据传递给 redux,而不用担心服务或异步的东西。

操作

// actions.js
function createRequestTypes(base) 
  return 
    REQUEST: base + "_REQUEST",
    SUCCESS: base + "_SUCCESS",
    FAILURE: base + "_FAILURE",
  


// Create lifecycle types on `RECIPES`
export const RECIPES = createRequestTypes("RECIPES")

// Create related actions
export const recipes = 
  // Notify the intent to fetch recipes
  request: request => (type: RECIPES.REQUEST, request)
  // Send the response
  success: response => (type: RECIPES.SUCCESS, response)
  // Send the error
  error: error => (type: RECIPES.FAILURE, error)

减速器

// reducer.js
import * as actions from "./actions"

// This reducer handles all recipes
export default (state = [], action) => 
  switch (action.type) 
    case actions.RECIPES.SUCCESS:
      // Replace current state
      return [...action.response]

    case actions.RECIPES.FAILURE:
      // Clear state on error
      return []

    default:
      return state
  

服务

我们还需要食谱 API。使用redux-saga 时,声明服务的最简单方法是创建一个(纯)函数,该函数将请求作为参数读取并返回Promise

// api.js
const url = "https://YOUR_ENPOINT";

export function fetchRecipes(request) 
  return fetch(url).then(response => response.json())

现在我们需要连接动作和服务。这就是redux-saga 发挥作用的地方。

// saga.js
import call, fork, put, take from "redux-saga/effects"
import * as actions from "./actions"
import * as api from "./api"

function* watchFetchRecipes() 
  while (true) 
    // Wait for `RECIPES.REQUEST` actions and extract the `request` payload
    const request = yield take(actions.RECIPES.REQUEST)

    try 
      // Fetch the recipes
      const recipes = yield call(api.fetchRecipes(request))

      // Send a new action to notify the UI
      yield put(actions.fetchRecipes.success(recipes))
     catch (e) 
      // Notify the UI that something went wrong
      yield put(actions.fetchRecipes.error(e))
    
  


function* rootSaga() 
  yield [
    fork(watchFetchRecipes)
  ]

就是这样!每当组件发送RECIPES.REQUEST 操作时,saga 就会连接并处理异步工作流。

dispatch(recipes.request(req))

redux-saga 的妙处在于,您可以在工作流程中轻松链接异步效果和调度操作。

【讨论】:

我在这里发表评论是因为我需要再次查阅此内容,而您的解释非常棒。【参考方案2】:

根据您的描述,您真正更新 UI 的唯一时间是在所有这些异步操作结束时 (B1)。

如果您不使用上述异步调用的结果来更改应用程序状态/更新您的 UI,那么拥有这些细粒度操作有什么好处?

当然有诸如“加载/请求开始”和“完成加载/请求停止”之类的东西,但在我看来,在你的情况下,你可以在 redux 之外进行链式异步调用(以某种方式API 层)并且只使用一个动作。 此操作调度一个“REQUEST_STARTED”,然后调用 API 层,该层执行 DB 调用和弹性搜索请求等,然后根据 promise 的结果调度“REQUEST_SUCCESS”或“REQUEST_FAILURE”,这将给出更新 UI 所需的数据。

这样,redux 中的状态只关注一个副作用,而不是链式调用的实现细节。此外,您的操作变得更加简单,因为它只处理一个异步调用的结果。

【讨论】:

以上是关于在 Redux 中链接多个异步调度的主要内容,如果未能解决你的问题,请参考以下文章

使用 typescript 将异步操作发送到 redux 调度

在redux和redux thunk中使用异步调度传递参数

如何使用 Redux Thunk 链接动态系列的异步操作?

Redux thunk:等待异步函数调度

如何在异步函数中调用 redux 调度作为函数体?

Redux - 调度异步操作时更改 URL