【问题标题】:Async/await redux thunk not returning promise to action correctly异步/等待 redux thunk 未正确返回对操作的承诺
【发布时间】:2019-04-18 21:06:01
【问题描述】:

我有一个使用 Axios 的 thunk,它使用 Sequelize 发布到 Express 路由。

路由正确发布(即数据被添加到数据库中),但 React 组件内部的操作未按预期运行。使用async/await,我希望该操作等到它完成数据库发布后再继续,但这里不是这种情况。我正在从行动中获得undefined

thunk 到达我正在调度操作以更新我的 redux 存储并返回响应的快速路由:

const addedNewList = (newList) => ({type: ADD_NEW_LIST, newList})

export const addNewList = (name, userId) => async dispatch => {
  try {
    const { data } = await axios.post('/api/list/add', { name, userId })
    dispatch(addedNewList(data))
    return data
  } catch (err) {
    console.error(err)
  }
}

使用调试器,我可以确认return data 实际上正在从我需要的服务器返回响应。我还可以确认 redux 商店正在正确更新。

但是在这里,当我尝试以result 访问该响应数据时,我得到undefined

  handleSubmit = async () => {
    const result = await this.props.addNewList(this.state.name, this.props.userId)

    // ** result is 'undefined' **

    this.handleClose()

    // pass off the results
  }

如果我在调用addNewList 操作后添加setTimeout,它会按预期工作。这向我暗示,也许它没有兑现承诺?但我的理解是,如果你在 thunk 中返回来自服务器的响应,它就会这样做。

为了完整起见,这是我的路线,我还通过调试器确认数据正在按预期传递:

const userAuth = function(req, res, next) {
  if (req.isAuthenticated()) {
    return next()
  }
  res.status(401).send('Unauthorized user')
}

router.post('/add', userAuth, async (req, res, next) => {
  const { name, userId } = req.body
  try {
    const list = await List.create({ name, userId })
    res.json(list)
  } catch(err) { next(err) }
})

为什么操作在handleSubmit 方法中返回undefined

【问题讨论】:

    标签: reactjs express redux sequelize.js


    【解决方案1】:

    尝试返回 dispatchaddedNewList(data) 代替:

    export const addNewList = (name, userId) => async dispatch => {
      try {
        const { data } = await axios.post('/api/list/add', { name, userId })
        return Promise.resolve(dispatch(addedNewList(data)));
      } catch (err) {
        console.error(err)
      }
    }
    

    话虽如此,您可以考虑重组组件以使用 mapStateToProps 来使用更新后的 Redux 存储中的值/result,而不是显式等待响应并手动传递值?

    【讨论】:

    • 你不应该使用 let 而不是 const 来等待,我以为我读到了一些关于 const 不适合这个的东西。
    • 我没听说过,但是如果你想提供一个解释原因的替代答案,它只能帮助解决这个问题。
    • @AlexanderStaroselsky 感谢您的帮助。我认为建议重组组件是正确的方向。在调用该操作后,我实际上在做的是获取新创建列表的 ID,并使用它通过history.push 显示更新的 url。但是我可以很容易地做到这一点,方法是把它移到组件渲染中,并在它准备好时使用 redux 存储来获取该信息。
    • @AlexanderStaroselsky 因此,如果我将下一步移至渲染并仅使用 mapStateToProps 更新状态,它实际上确实有效。您的回答是正确的,因为我真的只需要重组组件。在我这样做之后,用 Promise 包装调度变得不必要了。谢谢!
    • @jami0821 很高兴听到您能够解决它!您可能需要创建一个突出显示您具体所做的事情的答案,因为它可以帮助其他遇到类似情况的人。
    【解决方案2】:

    Alexander 的回复让我走上了正轨,所以我分享我的解决方案,以防它对某人有所帮助(正如他所建议的那样)。

    虽然我可以继续尝试通过将调度包装在 Promise 中来解决这个问题,但更好的解决方案是重新考虑组件的结构。

    在我的情况下,我想获取数据库中新创建行的 ID,以便将其传递给 history.push

      handleSubmit = async () => {
        const result = await this.props.addNewList(this.state.name, this.props.userId)
    
        this.handleClose()
    
        history.push(`/list/${result.id}`)
    }
    

    result 返回undefined,网址未正确更新。

    更好的解决方案是从更新数据的 redux 存储中访问新数据。这样我可以确定在数据准备好之前不会更新历史记录。

    所以我更新的组件现在看起来像这样,在newId 可用之前历史不会更新:

      handleSubmit = () => {
        this.props.addNewList(this.state.name, this.props.userId)
        this.handleClose()
      }
    
      render(){
        const { newId } = this.props
    
        if (newId) { 
          history.push(`/list/${newId}`)
        }
    
       return (
          ....
       )
      }
    }
    
    const mapStateToProps = (state) => {
      return {
        newId: state.list.newId
      }
    }
    

    除了将其放入渲染之外,我还可以使用 componentWillReceiveProps 或类似的组件生命周期方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-24
      • 2019-03-20
      • 2020-06-07
      • 2021-05-17
      • 1970-01-01
      • 2017-04-03
      • 2017-06-15
      • 2018-05-25
      相关资源
      最近更新 更多