当使用redux saga时,如何等待redux action完成分派?

4

我有一个redux saga设置,运行良好。其中一个调度是创建一个新订单,一旦创建完成,我想对更新的状态进行操作。

// this.props.userOrders = []

dispatch(actions.createOrder(object))

doSomethingWith(this.props.userOrders)

由于createOrder操作触发了redux saga调用API,存在延迟,因此在调用doSomethingWith函数之前this.props.userOrders不会被更新。我可以设置一个定时器,但这似乎不是一个可持续的想法。
我已经阅读了Stack Overflow上的类似问题,并尝试实现相关的方法,但似乎无法让它工作。我希望有人能够在我的下面的代码中添加几行来解决这个问题。
以下是相关的其他文件:
actions.js
export const createUserOrder = (data) => ({ type: 'CREATE_USER_ORDER', data })
Sagas.js
function * createUserOrder () {
  yield takeEvery('CREATE_USER_ORDER', callCreateUserOrder)
}

export function * callCreateUserOrder (newUserOrderAction) {
  try {
    const data = newUserOrderAction.data
    const newUserOrder = yield call(api.createUserOrder, data)
    yield put({type: 'CREATE_USER_ORDER_SUCCEEDED', newUserOrder: newUserOrder})
  } catch (error) {
    yield put({type: 'CREATE_USER_ORDER_FAILED', error})
  }
}

Api.js

export const createUserOrder = (data) => new Promise((resolve, reject) => {
  api.post('/userOrders/', data, {headers: {'Content-Type': 'application/json'}})
    .then((response) => {
      if (!response.ok) {
        reject(response)
      } else {
        resolve(data)
      }
    })
})

订单减速器:

case 'CREATE_USER_ORDER_SUCCEEDED':
   if (action.newUserOrder) {
      let newArray = state.slice()
      newArray.push(action.newUserOrder)
          return newArray
       } else {
       return state
   }
1个回答

4
这感觉像是一个XY问题。你不应该在组件的生命周期函数/事件处理程序中的任何时候"等待",而是利用存储的当前状态。
如果我理解正确,这是你当前的流程:
你在React组件中调度一个动作CREATE_USER_ORDER。这个动作被你的callCreateUserOrder saga所消耗。当你的创建订单saga完成时,它会派发另一个“完成”动作,你已经有了CREATE_USER_ORDER_SUCCEEDED
现在你应该添加适当的reducer/selector来处理你的CREATE_USER_ORDER_SUCCEEDED
这个CREATE_USER_ORDER_SUCCEEDED动作应该由你的reducer来处理,以创建一个新的状态,在你的状态中一些“订单”属性被填充。这可以通过选择器直接连接到你的组件,此时你的组件将被重新渲染,并且this.props.userOrders被填充。
例子:

组件

class OrderList extends React.PureComponent {
  static propTypes = {
    userOrders: PropTypes.array.isRequired,
    createOrder: PropTypes.func.isRequired,
  }

  addOrder() {
    this.props.createOrder({...})
  }

  render() {
    return (
      <Wrapper>
        <Button onClick={this.addOrder}>Add Order</Button>
        <List>{this.props.userOrders.map(order => <Item>{order.name}</Item>)}</List>
      </Wrapper>
    )
  }
}

const mapStateToProps = state => ({
  userOrders: state.get('userOrders'),
})

const mapDispatchToProps = {
  createOrder: () => ({ type: 'CREATE_ORDER', payload: {} }),
}

export default connect(mapStateToProps, mapDispatchToProps)(OrderList)

减速器

case 'CREATE_USER_ORDER_SUCCEEDED':
  return state.update('userOrders',
    orders => orders.concat([payload.newUserOrder])
  )

如果确实需要副作用,那么将这些副作用添加到您的 saga 中,或创建一个接收 SUCCESS 操作的新 saga。

感谢您抽出时间回复。非常抱歉,我忘记在问题中添加我的reducer了,现在我已经在底部添加了它。您所说的一切我都同意,并且我正在实际操作中。问题是this.props.userOrders没有被足够快地更新,我在代码的下一行使用它,因此我需要“等待”知道何时使用它。希望这样说得清楚。 - moinhaque
只是想澄清一下,渲染更新后的订单不是问题,问题在于在此之后立即处理this.props.userOrders。谢谢。 - moinhaque
@moinhaque,你能举个例子说明为什么你想要“等待”吗?我的意思是,你应该重新设计你的方法,以便你不需要“等待”。如果需要,使用另一个saga来执行成功操作。不要在组件内的同一函数中分派一个动作并立即等待它。 - noahnu
我正在使用该调度程序来创建一个新订单,然后我的下一步是为该订单创建付款,用户随后在应用程序内进行支付(这就是为什么我在组件中执行它的原因)。不确定添加另一个 Saga 是否会解决问题,但我会研究一下。 - moinhaque
一切都很好,但是想象一下,你有一个POST请求,它应该显示验证错误或导航到另一页... 并不是每个结果都应该触发render()... 那么如何使用dispatch/selectors处理这个问题:this.api.editObject.then(navigate...).catch(...validationErrors) - Thomas Stubbe
使用Redux-saga,您可以将API调用和处理程序放在saga中。验证错误从saga中分派,并减少状态,因此存储库包含错误消息。使用新的React hooks(甚至在选择不使用saga时,毕竟它是一种特定的设计模式),您将从组件内部处理API。 - noahnu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接