带有redux-observable

时间:2019-04-26 20:03:18

标签: redux rxjs redux-thunk reactivex redux-observable

我正在将我的react / redux应用重构为使用redux-observable而不是redux-thunk。使用thunk,我设置了一个api中间件,以使用CALL_API键侦听任何操作,并对数据进行一些操作,准备标头,准备完整的url,使用axios执行api调用,以及还可以进行一些与api调用相关的其他动作调度。

重要的是,api中间件调度了一个REQUEST_START操作,该操作为请求提供了一个ID,并在我状态的pending部分将其状态设置为network。当来自axios的承诺解决或拒绝时,中间件将调度REQUEST_END动作,更新状态,以便将当前请求设置为resolvedrejected。然后,响应将返回给最初分配CALL_API动作的调用动作创建者。

我无法弄清楚如何使用redux-observable来做到这一点。我想复制的有关上述api中间件的部分是REQUEST_STARTREQUEST_END操作调度。拥有一个集中的地方来处理所有与api调用相关的东西是非常方便的。我知道我可以在执行api调用的每个史诗中有效地调度REQUEST_STARTREQUEST_END动作,但是我不想在很多地方重复相同的代码。

我设法通过创建一个apiCallEpic来部分解决此问题,该监听器监听类型为CALL_API的动作,并为api调用进行了上述设置。但是,一个问题(或更确切地说,是我不喜欢的东西)是引发api调用的史诗(例如getCurrentUserEpic)实际上放弃了对apiCallEpic的控制。

因此,例如,当api调用成功并且有响应时,我可能希望在分派要由我的reducer处理的操作之前以某种方式格式化该响应数据。也就是说,getCurrentUserEpic应该对从api调用返回的数据进行某种格式化,然后再发送给reducer。通过传递在payloadHandler中定义的getCurrentUserEpic回调函数,{/ {1}}在成功获得响应时可以调用,可以实现接近此目的。但是,我不喜欢这种回调体系结构,而且似乎有一种更好的方法。

这里有一些代码演示了我如何使用thunk来使用api中间件。

apiCallEpic

这是我为使用import axios from 'axios'; // actionCreators.js // action types const CALL_API = "CALL_API"; const FETCH_CURRENT_USER = "FETCH_CURRENT_USER"; const RECEIVE_CURRENT_USER = "RECEIVE_CURRENT_USER"; // action creators for request start and end export const reqStart = (params = {}) => (dispatch) => { const reduxAction = { type: REQ_START, status: 'pending', statusCode: null, requestId: params.requestId, } dispatch(reduxAction); } export const reqEnd = (params = {}) => (dispatch) => { const { requestId, response = null, error = null, } = params; let reduxAction = {} if (response) { reduxAction = { type: REQ_END, status: 'success', statusCode: response.status, requestId, } } else if (error) { if (error.response) { reduxAction = { type: REQ_END, status: 'failed', statusCode: error.response.status, requestId, } } else { reduxAction = { type: REQ_END, status: 'failed', statusCode: 500, requestId, } } } dispatch(reduxAction); } // some api call to fetch data export const fetchCurrentUser = (params = {}) => (dispatch) => { const config = { url: '/current_user', method: 'get', } const apiCall = { [CALL_API]: { config, requestId: FETCH_CURRENT_USER, } } return dispatch(apiCall) .then(response => { dispatch({ type: RECEIVE_CURRENT_USER, payload: {response}, }) return Promise.resolve({response}); }) .catch(error => { return Promise.reject({error}); }) } // apiMiddleware.js // api endpoint const API_ENTRY = "https://my-api.com"; // utility functions for request preparation export const makeFullUrl = (params) => { // ...prepend endpoint url with API_ENTRY constant return fullUrl } export const makeHeaders = (params) => { // ...add auth token to headers, etc. return headers; } export default store => next => action => { const call = action[CALL_API]; if (call === undefined) { return next(action); } const requestId = call.requestId; store.dispatch(reqStart({requestId})); const config = { ...call.config, url: makeFullUrl(call.config), headers: makeHeaders(call.config); } return axios(config) .then(response => { store.dispatch(reqEnd({ response, requestId, })) return Promise.resolve(response); }) .catch(error => { store.dispatch(reqEnd({ error, requestId, })) return Promise.reject(error); }) } // reducers.js // Not included, but you can imagine reducers handle the // above defined action types and update the state // accordingly. Most usefully, components can always // subscribe to specific api calls and check the request // status. Showing loading indicators is one // use case. 完成类似操作而实现的代码。

redux-observable

任何评论或建议都值得赞赏!

1 个答案:

答案 0 :(得分:0)

我发现redux-fetch-epic-builder(用于构建“提取动作”的库以及由redux-observable处理的通用史诗)与您在此处尝试实现的内容类似(请注意,它使用的是rxjs 5,{{ 3}}进行救援)。它使用fetch而不是axios,但是替换起来很容易。此外,它还具有成功/失败操作的转换器。

该库有些陈旧,但是克服样板代码的基本思想仍然有效:通用的史诗生成器,可以通过对API的调用来获取数据。

我是React / Redux / RxJS的新手,但是我看到的redux-fetch-epic-builder唯一的问题是配置client的方式(按axios术语)。也就是说,我不完全满意(因为它不是FSA或RSAA):

//action creators
const getComments = (id, page = 1) => ({
    type: GET_COMMENTS,
    host: 'http://myblog.com',
    path: `/posts/${id}/comments`,
    query: {
        page,
    },
})
// ...
const epics = [
    buildEpic(GET_COMMENTS),
]

但这可能仍然是一种优雅的方式。并且许可证允许进一步开发该库。我没有将示例从库文档转换为与用户相关的示例,但是通过react-observable,肯定不需要引入单独的“ api中间件”。 (此外,我更喜欢/SUBACTION_SUBACTION好,但是改变并不容易。)