React-redux 项目 - 链式依赖异步调用不适用于 redux-promise 中间件?
React-redux project - chained dependent async calls not working with redux-promise middleware?
我是 redux 的新手,我正在尝试将 redux-promise 设置为中间件。我有这种情况,我似乎无法开始工作(当我只是尝试在不链接的情况下进行一个异步调用时,事情对我有用)
假设我有两个 API 电话:
1) getItem(someId) -> {attr1: something, attr2: something, tagIds: [...]}
2) getTags() -> [{someTagObject1}, {someTagObject2}]
我需要调用第一个,获取一个项目,然后获取所有标签,然后 return 一个包含该项目和与该项目相关的标签的对象。
现在我的action creator是这样的:
export function fetchTagsForItem(id = null, params = new Map()) {
return {
type: FETCH_ITEM_INFO,
payload: getItem(...) // some axios call
.then(item => getTags() // gets all tags
.then(tags => toItemDetails(tags.data, item.data)))
}
}
我在 toItemDetails 中有一个 console.log,我可以看到当调用完成时,我们最终进入 toItemDetails 并得到正确的信息。但是,看起来我们在调用完成之前就到达了 reducer,而我只是从 reducer 获得了一个未定义的有效负载(它不会再试一次)。对于这种情况,reducer 只是试图 return action.payload。
我知道链式调用不是很好,但我至少希望它能正常工作。这是仅通过 redux-promise 就可以完成的事情吗?如果没有,将不胜感激如何获得此功能的任何示例!
我用占位符函数填充了你丢失的代码,它对我有用——我的有效负载最终包含一个承诺,该承诺解析为 toItemDetails
的 return 值。所以可能是您未在此处包含的代码中的某些内容。
function getItem(id) {
return Promise.resolve({
attr1: 'hello',
data: 'data inside item',
tagIds: [1, 3, 5]
});
}
function getTags(tagIds) {
return Promise.resolve({ data: 'abc' });
}
function toItemDetails(tagData, itemData) {
return { itemDetails: { tagData, itemData } };
}
function fetchTagsForItem(id = null) {
let itemFromAxios;
return {
type: 'FETCH_ITEM_INFO',
payload: getItem(id)
.then(item => {
itemFromAxios = item;
return getTags(item.tagIds);
})
.then(tags => toItemDetails(tags.data, itemFromAxios.data))
};
}
const action = fetchTagsForItem(1);
action.payload.then(result => {
console.log(`result: ${JSON.stringify(result)}`);
});
输出:
result: {"itemDetails":{"tagData":"abc","itemData":"data inside item"}}
为了在第二步中访问item
,您需要将其存储在fetchTagsForItem
函数范围内声明的变量中,因为两个.then
s 本质上是兄弟姐妹:两者都可以访问封闭范围,但对 .then
的第二次调用将无法访问第一个中声明的变量。
关注点分离
创建您发送到 Redux 的操作的代码也在进行多个 Axios 调用并处理 returned 数据。这使得阅读和理解变得更加复杂,并且使处理 Axios 调用中的错误等操作变得更加困难。我建议把事情分开。一个选项:
- 将任何调用 Axios 的代码放在它自己的函数中
- 将
payload
设置为该函数的 return 值。
- 将该函数和所有其他调用 Axios 的函数移动到一个单独的文件(或一组文件)中。该文件成为您的 API 客户端。
这看起来像:
// apiclient.js
const BASE_URL = 'https://yourapiserver.com/';
const makeUrl = (relativeUrl) => BASE_URL + relativeUrl;
function getItemById(id) {
return axios.get(makeUrl(GET_ITEM_URL) + id);
}
function fetchTagsForItemWithId(id) {
...
}
// Other client calls and helper funcs here
export default {
fetchTagsForItemWithId
};
您的操作文件:
// items-actions.js
import ApiClient from './api-client';
function fetchItemTags(id) {
const itemInfoPromise = ApiClient.fetchTagsForItemWithId(id);
return {
type: 'FETCH_ITEM_INFO',
payload: itemInfoPromise
};
}
我是 redux 的新手,我正在尝试将 redux-promise 设置为中间件。我有这种情况,我似乎无法开始工作(当我只是尝试在不链接的情况下进行一个异步调用时,事情对我有用)
假设我有两个 API 电话:
1) getItem(someId) -> {attr1: something, attr2: something, tagIds: [...]}
2) getTags() -> [{someTagObject1}, {someTagObject2}]
我需要调用第一个,获取一个项目,然后获取所有标签,然后 return 一个包含该项目和与该项目相关的标签的对象。
现在我的action creator是这样的:
export function fetchTagsForItem(id = null, params = new Map()) {
return {
type: FETCH_ITEM_INFO,
payload: getItem(...) // some axios call
.then(item => getTags() // gets all tags
.then(tags => toItemDetails(tags.data, item.data)))
}
}
我在 toItemDetails 中有一个 console.log,我可以看到当调用完成时,我们最终进入 toItemDetails 并得到正确的信息。但是,看起来我们在调用完成之前就到达了 reducer,而我只是从 reducer 获得了一个未定义的有效负载(它不会再试一次)。对于这种情况,reducer 只是试图 return action.payload。
我知道链式调用不是很好,但我至少希望它能正常工作。这是仅通过 redux-promise 就可以完成的事情吗?如果没有,将不胜感激如何获得此功能的任何示例!
我用占位符函数填充了你丢失的代码,它对我有用——我的有效负载最终包含一个承诺,该承诺解析为 toItemDetails
的 return 值。所以可能是您未在此处包含的代码中的某些内容。
function getItem(id) {
return Promise.resolve({
attr1: 'hello',
data: 'data inside item',
tagIds: [1, 3, 5]
});
}
function getTags(tagIds) {
return Promise.resolve({ data: 'abc' });
}
function toItemDetails(tagData, itemData) {
return { itemDetails: { tagData, itemData } };
}
function fetchTagsForItem(id = null) {
let itemFromAxios;
return {
type: 'FETCH_ITEM_INFO',
payload: getItem(id)
.then(item => {
itemFromAxios = item;
return getTags(item.tagIds);
})
.then(tags => toItemDetails(tags.data, itemFromAxios.data))
};
}
const action = fetchTagsForItem(1);
action.payload.then(result => {
console.log(`result: ${JSON.stringify(result)}`);
});
输出:
result: {"itemDetails":{"tagData":"abc","itemData":"data inside item"}}
为了在第二步中访问item
,您需要将其存储在fetchTagsForItem
函数范围内声明的变量中,因为两个.then
s 本质上是兄弟姐妹:两者都可以访问封闭范围,但对 .then
的第二次调用将无法访问第一个中声明的变量。
关注点分离
创建您发送到 Redux 的操作的代码也在进行多个 Axios 调用并处理 returned 数据。这使得阅读和理解变得更加复杂,并且使处理 Axios 调用中的错误等操作变得更加困难。我建议把事情分开。一个选项:
- 将任何调用 Axios 的代码放在它自己的函数中
- 将
payload
设置为该函数的 return 值。 - 将该函数和所有其他调用 Axios 的函数移动到一个单独的文件(或一组文件)中。该文件成为您的 API 客户端。
这看起来像:
// apiclient.js
const BASE_URL = 'https://yourapiserver.com/';
const makeUrl = (relativeUrl) => BASE_URL + relativeUrl;
function getItemById(id) {
return axios.get(makeUrl(GET_ITEM_URL) + id);
}
function fetchTagsForItemWithId(id) {
...
}
// Other client calls and helper funcs here
export default {
fetchTagsForItemWithId
};
您的操作文件:
// items-actions.js
import ApiClient from './api-client';
function fetchItemTags(id) {
const itemInfoPromise = ApiClient.fetchTagsForItemWithId(id);
return {
type: 'FETCH_ITEM_INFO',
payload: itemInfoPromise
};
}