Redux: 等待多个异步调用的redux习惯用法是什么?

创建于 2015-09-14  ·  30评论  ·  资料来源: reduxjs/redux

当我启动react组件时,我需要进行2个不同的异步http调用,并等待它们的结果。
理想情况下,这两个调用会将其数据放入2个不同的reducer中。

我了解进行单个异步调用的模式。
但是,我看不到进行多个异步调用,使它们并行执行并等待其结果均到达的任何好的示例。

这样做的redux方法是什么?

question

最有用的评论

我假设您使用Redux Thunk中间件。

声明一些动作创建者:

import 'babel-core/polyfill'; // so I can use Promises
import fetch from 'isomorphic-fetch'; // so I can use fetch()

function doSomething() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING, json }),
      err => dispatch({ type: SOMETHING_FAILED, err })
    );
}

function doSomethingElse() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
}

请注意,我如何通过将链保持为thunk动作创建者内部函数的返回值来最终返回Promise 。 这让我做到了:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

当您使用Redux Thunk时, dispatch返回您从thunk动作创建者(在本例中为Promise)返回的函数的返回值。

这使您可以使用诸如Promise.all()来等待多个操作的完成:

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

但是,最好定义另一个动作创建者,该动作创建者充当先前动作创建者的_composition_,并在内部使用Promise.all

function doEverything() {
  return dispatch => Promise.all([
    dispatch(doSomething()),
    dispatch(doSomethingElse())
  ]);
}

现在你可以写

store.dispatch(doEverything()).then(() => {
  console.log('I did everything!');
});

调度愉快!

所有30条评论

我假设您使用Redux Thunk中间件。

声明一些动作创建者:

import 'babel-core/polyfill'; // so I can use Promises
import fetch from 'isomorphic-fetch'; // so I can use fetch()

function doSomething() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING, json }),
      err => dispatch({ type: SOMETHING_FAILED, err })
    );
}

function doSomethingElse() {
  return dispatch => 
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
}

请注意,我如何通过将链保持为thunk动作创建者内部函数的返回值来最终返回Promise 。 这让我做到了:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

当您使用Redux Thunk时, dispatch返回您从thunk动作创建者(在本例中为Promise)返回的函数的返回值。

这使您可以使用诸如Promise.all()来等待多个操作的完成:

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

但是,最好定义另一个动作创建者,该动作创建者充当先前动作创建者的_composition_,并在内部使用Promise.all

function doEverything() {
  return dispatch => Promise.all([
    dispatch(doSomething()),
    dispatch(doSomethingElse())
  ]);
}

现在你可以写

store.dispatch(doEverything()).then(() => {
  console.log('I did everything!');
});

调度愉快!

@gaearon是否有任何特定原因导致您偏爱thunk不是promise中间件? 或关于何时使用的建议? 谢谢。

没有特别的原因,我对此更加熟悉。
同时尝试并使用最适合您的方法!

明白了,谢谢!

JFYI :)
screen shot 2015-09-14 at 11 06 42 am

如果您愿意,可以看一下它https://github.com/Cron-J/redux-async-transitions

@gaearon快速问题-查看上面的示例,您在其中调用:

store.dispatch(doSomething()).then(() => {
  console.log('I did something');
});

-即使doSomething被拒绝,看起来“我做了某事”块也会被击中-因为您不会在捕获到错误后将错误重新抛出doSomething链中。 那是故意的吗? 如果不是-如果您将错误扔回那里-您会在调用方中再次处理它吗?

这只是一个例子,我并不是要把它当作权威性的参考文献,而只是一种看待它的方式。 我没有认真思考一下。

您知道如何退还动作创建者的承诺,其余的取决于您。 是否重新抛出错误,在哪里处理错误完全取决于您。 这并不是真正的Redux问题,因此我的建议与其他任何人一样都很好,在这种情况下,您比我将拥有更多关于要构建的内容和风格的上下文。

太好了,谢谢! 想到了这种情况,但想确保没有我忽略的魔力。

由于Redux是可自定义的,因此不能保证dispatch返回值是预期的Promise。

实际上,自从我采用Redux循环以来,我在现实生活中遇到了一个问题。 它调整dispatch返回一个包装好的Promise(https://github.com/raisemarketplace/redux-loop/blob/master/modules/effects.js#L38)。 因此,我们不能依赖dispatch返回值。

好吧,正如Dan在此线程中所说的:它是_your_应用,_you_正在编写动作创建者,因此_you_可以决定他们返回的内容。

@markerikson不能保证dispatch返回值是操作创建者的返回值,因为中间件可能会对其进行调整。

但是,我有一个解决方法。 代替在then上链接dispatch(actonCreator()) ,我们可以在actionCreator()

是的,中间件可以进行调整,但是我的意思是_you_正在您自己的应用程序中设置中间件:)

@gaearon ,快速(希望相关)的问题! 您对做类似的事情有何看法

function fetchDataBWhichDependsOnA() {
  return dispatch => {
    dispatch(fetchDataA())
      .then(() => {
        fetch('/api/get/data/B')
          .then(dispatch(receiveDataB(response.data)))
      })
  }
}

在此代码段中有2个嵌套的异步操作,它们可以工作,但是我在测试它时遇到了麻烦。 我能够测试fetchDataA()因为它只有1级的异步调用。 但是,当我尝试为fetchDataBWhichDependsOnA()编写测试时,我无法从then块中的store中检索最新值。

@timothytong您没有兑现承诺。 如果您从dispatch和嵌套的fetch调用返回结果,则应该可以在测试中等待,直到整个链解决。

@johanneslumpe不错,谢谢队友! 现在剩下的就是用异步获取链处理数据依赖性的问题了吗?

我遇到了同样的问题,花了30分钟尝试提出解决方案。 我尚未完成此实现,但是我觉得它的方向正确。

https://github.com/Sailias/sync-thunk

我的想法是,组件可以请求填充某些redux状态,如果不是,则应引用映射以调用操作以填充它们并用then链接它们。 链中的每个动作都不需要传入数据,它可以从状态中获取数据,因为相关动作会填充该数据。

@gaearon

Promise.all([
  store.dispatch(doSomething()),
  store.dispatch(doSomethingElse())
]).then(() => {
  console.log('I did everything!');
});

我对此有疑问。 很抱歉,如果它是如此基本。

在这种情况下,将有两个调度。 这意味着将两次渲染该组件。 但是,如果组件希望两个数据都显示有意义的东西怎么办?

因此,在那种情况下,我希望组件仅渲染一次,也就是所有异步动作完成时。

我得到了一些似乎正在处理此问题的中间件的名称,例如redux-batched-subscriberedux-batched-actions 。 但是,对最佳方法有些困惑。

如果这是一个有效的方案,那么建议的处理方法是什么?

@jintoppy如果您的要求是避免多次调用以渲染组件,则Promise.all解决方案将无济于事; 因为两个doSomething() and doSomething()都将触发状态更改并导致组件渲染的调度数据。

@jintoppy为什么您需要任何中间件来处理您的情况?
是什么阻碍了您这样做。

// action creator
function fetchAB() {
  return dispatch => {
    const fetchA = fetch( 'api/endpoint/A' );
    const fetchB = fetch( 'api/endpoint/B' );
    return Promise.all([ fetchA, fetchB ])
      .then( values => dispatch(showABAction(values)) )
      .catch( err => throw err );
  }
} 

您只需将所有请求放入一个动作创建者中,然后在所有动作都解决后才进行动作调度。 在这种情况下,您调用了reducer,因此状态仅更改了一次,因此render()可能也将仅触发一次。

@apranovich :我发现这种方法存在一个问题。 现在,我需要在一个动作中结合多个提取。 因此,如果有10个异步调用,则必须将所有这些都放在一个动作中。 另外,如果我以后要触发这10个异步调用中的一个,该怎么办? 基本上,采用这种方法,所有异步逻辑都将放在一个动作中。

谢谢大家提供的有用信息。 很快的问题是,如何实现相同的目标,但是取而代之的是当一次访存返回下一个访存方法所需的数据时。 例如,有一个免费的weather API端点,我在其中输入地理位置以获取ID(第一个fetch ),此ID是下一个fetch所必需的,以获取位置的天气。 我知道这是一个奇怪的用例,但我想知道如何处理类似情况。 提前致谢!

@gaearon ,您好,我想得到您对我这个问题的评论。 我必须对两个API进行嵌套的异步调用。 来自第二个API的响应取决于第一个API的响应。

export const getApi=()=>{
        return(d)=>{
        axios({
            method:'GET',
            url:Constants.URLConst+"/UserProfile",
            headers:Constants.headers
        }).then((response)=>{
            return d({
                type:GET_API_DATA,
                response,
                axios({
                    method:'GET',
                    url:Constants.URLConst+"/UserProfileImage?enterpriseId="+response.data.ProfileData.EnterpriseId,
                    headers:Constants.headers
                }).then((response1)=>{
                    return d({
                        type:GET_PROFILE_IMAGE,
                        response1
                    })
                })
            })
        }).catch((e)=>{
            console.log("e",e);
        })
    }

}

我尝试了类似上面的操作,但这不起作用。在我的项目中,我必须独立使用两个API的响应。
那么,正确的做法是什么?

谢谢。

@ c0b41 ,请检查更新的问题。

@ c0b41 ,这在第二个axios调用中给出了语法错误。

@ aayushis12@ c0b41 :这是一个错误跟踪器,而不是帮助论坛。 您应该尝试在Stack Overflow或Reactiflux上问这个问题。 将有更多的人看到这个问题并能够提供帮助。

对于那些经历

我做了一切

在解决所有异步调用之前将其打印出来,请检查您是从异步操作创建者返回语句还是表达式。

例如,如果在箭头函数中使用{}声明了doSomethingElse()

function doSomethingElse() {
  return dispatch => {
    fetch(
      '/api/something'
    ).then(
      response => response.json()
    ).then(
      json => dispatch({ type: DO_SOMETHING_ELSE, json }),
      err => dispatch({ type: SOMETHING_ELSE_FAILED, err })
    );
  }
}

doSomethingElse()解析之前,将打印“我做了一切”。 通过将{}放在=>后来解决此问题。

@gaearon,一个基于https://github.com/reduxjs/redux/issues/723#issuecomment -139927639的问题,如果您需要调度一个等待已分派两个操作的操作,则该如何处理?因为这些动作而改变了? 我有一种情况,其中调度了动作,但是then内部的动作已执行_before_状态已更改。 我们正在使用redux-thunk

@enmanuelduran :Dan不再是一个积极的维护者-请不要ping他。

不看代码就很难回答您的问题,但是我们的问题并不是要成为讨论论坛的。 如果将问题发布到Stack Overflow上会更好-您可能会更快地在那里获得更好的答案。

@markerikson哦,很抱歉,这不是我的意图,如果有人感兴趣的话,请在stackoverflow中提出一个问题: https :

非常感谢。

此页面是否有帮助?
0 / 5 - 0 等级