当我启动react组件时,我需要进行2个不同的异步http调用,并等待它们的结果。
理想情况下,这两个调用会将其数据放入2个不同的reducer中。
我了解进行单个异步调用的模式。
但是,我看不到进行多个异步调用,使它们并行执行并等待其结果均到达的任何好的示例。
这样做的redux方法是什么?
我假设您使用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 :)
@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-subscribe和redux-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 :
非常感谢。
最有用的评论
我假设您使用Redux Thunk中间件。
声明一些动作创建者:
请注意,我如何通过将链保持为thunk动作创建者内部函数的返回值来最终返回
Promise
。 这让我做到了:当您使用Redux Thunk时,
dispatch
返回您从thunk动作创建者(在本例中为Promise)返回的函数的返回值。这使您可以使用诸如
Promise.all()
来等待多个操作的完成:但是,最好定义另一个动作创建者,该动作创建者充当先前动作创建者的_composition_,并在内部使用
Promise.all
:现在你可以写
调度愉快!