Redux 和 isFetching 中的顺序异步操作

Posted

技术标签:

【中文标题】Redux 和 isFetching 中的顺序异步操作【英文标题】:Sequential async actions in Redux and isFetching 【发布时间】:2018-04-28 09:45:39 【问题描述】:

我的 React 组件需要异步获取一些数据 A,然后根据其内容,发送第二个异步请求来获取数据 B。所有结果都存储在 Redux 中,我们使用 Redux-thunk。

页面上可能同时有几个组件都需要A,所以很有可能它已经存在于Redux;但另一个组件也可以获取它,然后isFetching 为真。我不想有多个相同的请求(尤其是因为浏览器取消了它们)。

像https://github.com/reactjs/redux/issues/1676 和https://github.com/reactjs/redux/issues/723 这样的顺序操作的解决方案提出了一个redux-thunk,它返回一个promise,如果对象已经存在,这个promise 就已经解决了;例如:

function getA(uuid) 
    return (dispatch, getState) => 
        const currentA = getState().a[uuid];

        if (currentA) 
            // Return resolved promise with the already existing object
            return Promise.resolve(currentA);
         else 
            // Return async promise
            return goFetchA(uuid).then(objectA => 
                dispatch(receivedA(uuid, objectA));
                return objectA;
            );
        
    ;


function getAthenB(uuidA, uuidB) 
    return dispatch => 
        dispatch(getA(uuidA)).then(
            objectA => dispatch(getB(objectA, uuidB)));

到目前为止,一切都很好。但是,如果状态同时包含对象和“isFetching”布尔值,我可以返回什么样的承诺?如果我们可以将请求的实际 Promise 存储在状态中,这将是微不足道的,但这种事情不应该进入 Redux 状态。

function getA(uuid) 
    return (dispatch, getState) => 
        const currentA = getState().a[uuid];

        if (currentA) 
            if (!currentA.isFetching) 
                return Promise.resolve(currentA.data);
             else 
                // WHAT TO RETURN HERE?
            
         else 
            dispatch(startFetchingA(uuid));
            return goFetchA(uuid).then(objectA => 
                receivedObjectA(uuid, objectA);
                return objectA;
            );
        
    ;

当我想取消一个正在进行的请求时也存在类似的问题——它没有存储在任何地方,所以一个也有助于解决这个问题的解决方案是理想的。

【问题讨论】:

getA 的thunk 函数(dispatch, getState) => ... 中,为什么会返回非操作?看来您应该改为在操作对象中使用 promise/objectA 调用 dispatch @bsapaka:thunk函数的返回值是dispatch()返回的;我承诺它可以在 getAthenB 中使用。据我所知,动作(和 Redux 状态)应该是纯字符串、对象等,而不是承诺? 我的想法是从我链接到的示例中返回这些承诺。 我现在正在使用一种解决方法——我有一个外部组件,它确保所有必需的 As 都存在于 Redux 状态中,并且当它们这样做时,它会呈现一个可以假设 As 的内部组件呈现并获取丢失的 B。这行得通,但我仍然想知道是否有更好的方法。 【参考方案1】:

我相信,如果不引入一些可变的、不可序列化的存储,它将存储正在运行的请求,以及纯的、完全可序列化的 Redux 存储,就不可能处理这种情况。

有很多库可以帮助您处理这种副作用(Redux Saga 和 Redux Observable,仅举几例)。但是,如果您的情况仅限于您刚才描述的情况,我建议使用 Redux Thunk 的 extra argument feature,我相信它是专为此类情况设计的。

快速了解如何使用额外参数实现任务:

/**
 * init.js
 */
const api = ...; // creating api object here
const requests = ;

const store = createStore(
  reducer,
  applyMiddleware(thunk.withExtraArgument(api, requests))
)

/**
 * actions.js
 */
function getA(uuid) 
    return (dispatch, getState, api, requests) => 
        if (requests[uuid]) return requests[uuid];

        const currentA = getState().a[uuid];
        if (currentA) 
             return Promise.resolve(currentA.data);
        

        dispatch(startFetchingA(uuid));
        const request = requests[uuid] = api.fetchA(uuid);
        return request.then(objectA => 
            delete requests[uuid];
            receivedObjectA(uuid, objectA);
            return objectA;
        );
    ;

同样的技术可以用来取消请求。

此外,您可能会通过引入custom Redux middleware 来获得更清洁的解决方案,但这实际上取决于您真正的成熟案例,而不是这个简化的案例。

【讨论】:

谢谢,深思!

以上是关于Redux 和 isFetching 中的顺序异步操作的主要内容,如果未能解决你的问题,请参考以下文章

react redux中用户提要中没有帖子时如何显示消息?

如何使用 Redux Thunk 链接动态系列的异步操作?

使用 Redux + React Router 的 React App 中的异步数据流?

Redux 中的异步/等待

安装了 redux-thunk 的 redux 操作中的异步函数错误

为啥我们需要中间件用于 Redux 中的异步流?