【问题标题】:Nesting API calls in React Redux在 React Redux 中嵌套 API 调用
【发布时间】:2018-10-29 20:31:13
【问题描述】:

我很难弄清楚我的动作创建器中正在发生(和未发生)的事情。

我需要调用一个 API 端点,获取所有返回项目的 ID 和名称,然后为每个 ID 进行另一个调用。我想将最后一次调用的返回和第一次调用的 ids/names 存储在一个对象中并调度它。

{
  category: name //name of category
  subcategory: [] //array of categories in the category above. 
}

现在,我的 reducer 最终得到了我想要的东西,但是当我尝试在组件中记录那个特定的 prop 时,它是空的。 (下面我使用的是 OpenSocialAPI 或 osapi。这只是 ajax 请求的基本包装。允许我不必进行身份验证,因为它看到我已经通过身份验证。)

export function fetchCategories(id){
  let categories = []
  return function(dispatch){
    dispatch(requestCategories(id))
    return osapi.core.get({
      v: "v3",
      href: "/places/" + id + "/places"
    }).execute(function(response) {
      response.content.list.forEach(function(category) {
        osapi.core.get({
          v: "v3",
          href: "/places/" + category.id+ "/categories"
        }).execute(function(response) {
          categories.push({
            category: category.name,
            subcategories: response.content.list.map(category => category.name)
          })
        })
      })

    console.log("Category response: ", categories)
    dispatch(receiveCategories(id, categories))
    })
  }
}
export function receiveCategories(id,array){
  return {
    type: RECEIVE_CATEGORIES,
    id,
    categories: array,
    recievedAt: new Date(Date.now()),
    isFetching: false
  }
}

在我的应用程序中,我在 componentDidMount 中调度动作创建者

  componentDidMount() {
    const { dispatch } = this.props
    dispatch(fetchCategoriesIfNeeded(id))
  }

现在,当我控制台登录我的 Category 组件并在上面执行时,它是空的。但是在我的记录器中查看我的状态,当recieveCategories 完成时,我有我想要的对象数组

[{category:..., 
  subcategories:[...]
 },
 {category:..., 
  subcategories:[...]
 }]

我怀疑这是因为异步的原因,但我不确定如何继续。

我尝试为基于 Promise 的调用创建自己的包装器,但我遇到了类似的问题,可能更多是因为我不确定 resolve(response) 是否是我想要的。

function httpService(path){
   return new Promise(function(resolve, reject){
          osapi.core.get({
              v: 'v3',
              href: path
          }).execute(function(response, error){
              if(error){
                  return reject(new Error("Error: ", error))
              }
              resolve(response)
          })  
        })
}
export function fetchCategories(spaceId) {
  let categories = []
  return function(dispatch) {
    dispatch(requestCategories(id))
    return httpService("/places/" + id + "/places")
      .then(function(response) {
        response.content.list.forEach(function(category) {
          fetchSubCategories("/places/" + category.id + "/categories")
            .then(function(response) {
              categories.push({
                category: category.name,
                subcategories: response
              })
            })
        })
        console.log("CATEGORIES: ", categories)
        dispatch(receiveCategories(id, categories))
      })
  }
}
function fetchSubCategories(url){
  return httpService(url)
  .then(function(response){
    return response.content.list.map(category => category.name)
  })
}

你能看看这个并给予指导吗?另外,我调度一个基于 API 响应构建的数组是一种有效的做事方式,还是有更好的方法?谢谢

我只能找到 1 个具有类似用例的其他问题,但他们使用的是 bluebird 或类似的东西。我真的很想保留这个,除了 Redux 之外没有任何额外的东西。

【问题讨论】:

  • 你使用的是 redux-thunk,对吧?
  • 是的,我正在使用 redux-thunk。我的道具被正确映射,因为我正在修改以前只查看 1 层类别的动作,现在需要更深入地查看。
  • 您使用的这个抓取库是什么?如果不了解.execute() API 是什么,就很难编写代码来解决这个问题。
  • .execute 只是 osapi 的 .then。
  • 如果可能的话,你应该切换到 redux-saga

标签: javascript reactjs redux react-redux


【解决方案1】:

看起来您只需要在您的.execute() 回调内部调度您的类别,而不是在它之外。您正在执行osapi.core.get().execute((response) =>,但随后在该execute 回调之外,您调度receiveCategories,它将在您的Promise 解决之前很久执行,并调度您初始化的空数组。

您还需要使用Promise.all 来获取所有嵌套GET 请求的响应。

也没有理由保留一个变异数组。

【讨论】:

  • 有趣。用另一双眼睛看事物总是很棒的。我在最外层执行了调度。因为它是在一个循环中,所以调度自然会运行第一个响应的长度。我会简单地将 Promise.all 包裹在第二个 osapi.core.get() 周围吗?另外,您提到不保留变异数组,您的意思是类别数组?你建议最后删除它吗?或者用另一种方法来构建该 reducer 数组而不是定义一个?
  • Promise.all 接受一个 promise 数组,并行运行它们,并返回一个包含所有值的数组。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…。你不需要使用forEachpush,只需将最终结果作为map返回到你需要的数据结构中
  • 啊,我明白了。谢谢!我会看看它并试一试。
  • 很难从 execute() api 中分辨出来,您可能需要“承诺”它以使其与 Promise.all 配合得很好,就像制作一个正确调用 resolve() 的 Promise 包装器或reject() 并遵循标准的 promise api,而不是自制的
  • 使用 Promise.all 能够做到这一点!有趣的学习!我不得不使用数组变量来存储承诺。谢谢!
【解决方案2】:

我猜osapi.core.get 是某种基于承诺的获取库?并且获取成功时调用.execute

如果是这样,那么您缺少的是您没有等待所有异步调用完成。

我将展示一个基于通用 fetch 和原生 Promises 的解决方案,以便您了解该解决方案并根据您的特定库采用它。

const promises = [];
response.content.list.forEach(function(category) {
    const promise = fetch("/places/" + category.id+ "/categories");
    promises.push(promise);
})

Promise.all(promises).then(responses => {        
    categories = responses.map(response => ({/* do your object mapping here */}))
    // now you can dispatch with all received categories
    dispatch(receiveCategories(id, categories))
}); 

此外,您在嵌套函数中使用了相同的变量 - 虽然这可能有效并且计算机可以理解它,但任何人都很难确定哪个 response 属于哪个范围。因此,您可能还想再看看您的变量名称。

【讨论】:

  • 是的,执行成功时调用。谢谢你写出来!我会看看这个和 Promise.all 提到的。另外,我更改了 SO 的变量名只是因为它们很敏感,不过感谢您保持诚实!稍后我会报告事情的进展情况。
  • 酷!如果对您有帮助,请不要忘记为答案投票。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-27
  • 1970-01-01
  • 1970-01-01
  • 2019-02-21
  • 2018-08-30
  • 1970-01-01
  • 2017-08-08
相关资源
最近更新 更多