在Redux中链接多个异步调度

时间:2016-04-17 20:57:03

标签: reactjs redux redux-thunk

我试图以下列方式将多个动作链接在一起:

一个。将用户数据发布到数据库

B中。使用发布的数据来查询Elasticsearch的结果

(我同时做A和B)

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

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

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

这个,以及" requestRecipes"和#34; receiveRecipes"因为其他动作创建者似乎只是为了进行一次异步调用。 (请求,接收和获取功能)

总结:当你链接2-3个异步动作时,它们的输出相互依赖(我需要尽可能地宣传),有没有更有效的方法这样做而不为每个异步调用编写3个函数?

我认为必须要有办法。我正在使用Redux文档进行模式匹配,很快就对我正在创建的函数感到非常不知所措

非常感谢您的反馈!

2 个答案:

答案 0 :(得分:8)

您可以使用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动作时,该传奇将挂钩并处理异步工作流。

dispatch(recipes.request(req))

redux-saga令人敬畏的是,您可以在工作流程中轻松链接异步效果和分派操作。

答案 1 :(得分:1)

根据您的描述,您实际更新UI的唯一时间就在所有这些异步操作的最后(B1)。

如果您不使用前面异步调用的结果来更改您的应用程序状态/更新您的UI,那么进行这些细粒度操作会带来什么好处?

当然有&#34; loading / request started&#34;并且&#34;完成加载/请求已停止&#34;,但在我看来,在您的情况下,您可以在redux之外(在某种API层中)执行链式异步调用,并且只使用一个操作。 此操作调度&#34; REQUEST_STARTED&#34;,然后调用API层,执行DB调用和弹性搜索请求等,然后调度&#34; REQUEST_SUCCESS&#34;或者&#34; REQUEST_FAILURE&#34;,基于承诺的结果,它将为您提供更新UI所需的数据。

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