0

我使用 redux-api-middleware 进行 api 调用,使用 redux-sagas 进行副作用。对于特定的表单流,用户可以进行 2 种不同的更改,这些更改需要由单独的 api 处理。如果我们其中一个失败了,整个传奇就应该失败。我有一个单独的传奇来处理失败。在某些情况下,这会中断。当我们查看开发人员工具时,saga 似乎运行了两次,跳到初始点,然后再次运行这些步骤。我已将此标记为评论。触发 saga 的动作只运行一次(在 redux 开发工具中验证)

这是模拟代码 -

行动 -

function changesAApi() {
  return (dispatch, getState) => {
    dispatch({
      [CALL_API]: {
        types: ['changes-A', 'changes-A-success','changes-A-failure'],
        endpoint: '/changesA',
        method: 'post',
      }
    })
  }
}

// can be called individually, but state in A affects this call
function changesBApi() {
  return (dispatch, getState) => {
    dispatch({
      [CALL_API]: {
        types: ['changes-B', 'changes-B-success','changes-B-failure'],
        endpoint: '/changesB',
        method: 'post',
      }
    })
  }
}

萨迦 -

function* saveFormChanges(action) {
  // point (1)
  if (action.changesA) {
    yield put(changesAApi());
    // jumps to point (1) the first time
    yield take('changes-A-success');
    // point (2)
  }
  if (action.changesB) {
    yield put(changesBApi());
    // jumps to point (2)
    yield take('changes-B-success');
    // point (3)
  }
  // jumps to point (3)
  yield call(displaySuccessMessage);
}

function* errorSaga() {
  yield takeLatest(action => /failure$/.test(action.type), () => {
    alert('failure');
  });
}

这是创建此流程的正确方法吗?有一个更好的方法吗?

4

1 回答 1

0

老实说,我会以完全不同的方式来构建它。对我来说,问题似乎在于工具太多。简单的解决方案是使用其中一个。就个人而言,我喜欢在我的所有 API 调用中使用 saga,因为它们让我可以更好地控制我的 API 的使用方式。

sagas 文档提供了一个关于 sagas 如何工作的基本示例。这是一个快速启动和运行的非常棒的教程,但是一旦我了解了编写 sagas 的正确方法,我发现 sagas 的限制要少得多。

以这种方式编写 saga 使您能够以更优雅的方式处理异步状态更改,并大大提高代码的可读性。

这是我整理的一个简短的小片段,说明了您的问题的一种可能的解决方案:

// dispatch from component
const getUsers = () => dispatch({type: 'GET_USERS'})

function* watchGetUsers() {

    while(true) {
        try{
            yield take('GET_USERS')
            yield put({ type: 'REQUEST/GET_USERS' })

            const data = yield call(methodThatCallsAPI, args)
            // side effects stuff
            yield put({ type: 'SUCCESS/GET_USERS', payload: data})
            // redux reducer handles changing state
        } catch (err) {
            yield put({ type: 'FAILURE/GET_USERS', payload: err })
            // redux reducer handles changin state
            // coul also just use put to pass logic to error saga
        }

    }
}

您还可以使用 sagas 阻止任何已发出的并发请求或取消未完成的请求。这是另一个如何使 sagas 阻塞的示例:

// put sagas into an object
const apiSagas = {
    'GET_USERS': watchGetusers,
    //  other sagas
}

function* consumeApiRequestsBlocking() {
    while(true) {
        // dispatch({ type: 'API_REQUEST', payload: {type: GET_USERS}})
        const request = yield take('API_REQUEST')

        yield call(apiSagas[request.type])
        // 'API_REQUEST' cannot be dispatched until above line finishes
        // since call is blocking; fork is non-blocking

        // NOTE: you sould remove the yield take('GET_USERS') from 
        // watchgetUsers in this example
    }
}

祝你好运!

于 2017-07-17T21:50:05.923 回答