Redux 和 isFetching 中的顺序异步操作

Rem*_*ich 5 redux redux-thunk react-redux

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

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

https://github.com/reactjs/redux/issues/1676https://github.com/reactjs/redux/issues/723等顺序操作的解决方案提出了一种返回承诺的 redux-thunk,该承诺已经是如果对象已经存在,则已解决;例如:

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)));
}
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好。但是,如果状态同时包含对象和“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;
            });
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

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

fku*_*kov 4

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

有很多库可以帮助您处理这种副作用(例如Redux SagaRedux Observable )。但如果你的情况仅限于你刚才描述的情况,我建议使用Redux Thunk 的额外参数功能,我相信它是专门为这种情况设计的。

快速概述如何使用额外的参数来实现您的任务:

/**
 * 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;
        });
    };
}
Run Code Online (Sandbox Code Playgroud)

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

另外,您可能可以通过引入自定义 Redux 中间件来获得更干净的解决方案,但这实际上取决于您真正成熟的情况,而不是这个简化的情况。