如何结合Redux Offline与RTK-Query处理离线请求
Redux Offline 与 RTK Query 结合实现方案
已经有成熟的生产实践实现二者结合,核心需求(离线时失败的POST类mutation入队、联网后自动重放)不需要硬套传统Redux Offline手动编写带offline meta的action的模式——RTK Query的mutation是内部自动生成action、托管请求生命周期的,手动给触发的action加offline meta会打断RTK Query自带的loading、缓存更新逻辑,适配思路是复用Redux Offline的网络状态监听、离线队列能力,请求发送和状态管理完全交给RTK Query处理。
具体实现步骤
- 保留Redux Offline基础配置,保留其outbox队列、网络状态检测核心逻辑,关闭其自带的默认请求发送effect,避免和RTK Query的请求逻辑冲突。
- 封装离线感知的自定义baseQuery:请求发起前先读取store中Redux Offline维护的网络状态,如果当前离线,就把当前mutation的endpoint名称、请求参数存入Redux Offline的outbox队列,同时返回一个持续pending的Promise,让UI层维持加载状态,不直接抛出请求失败错误。
- 监听store状态变化,当检测到网络从离线切为在线、且outbox队列中存在待处理的RTK Query mutation记录时,依次用存储的endpoint和参数重新触发对应mutation,请求完成后将对应记录从队列中移除。
- 乐观更新、请求成功/失败的状态回滚直接使用RTK Query自带的
onQueryStarted生命周期钩子实现,不需要手动编写commit、rollback类的action,RTK Query会自动管理缓存更新与回滚。
核心实现代码示例
离线感知的自定义baseQuery与API定义
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' import { offlineActions } from '@redux-offline/redux-offline' const baseQuery = fetchBaseQuery({ baseUrl: '/api' }) const offlineAwareBaseQuery = async (args, api, extraOptions) => { const isOnline = api.getState().offline.online // 离线状态将请求加入队列 if (!isOnline) { api.dispatch(offlineActions.outbox.enqueue({ type: 'RTK_QUERY_PENDING_MUTATION', meta: { rtkQuery: { endpointName: api.endpoint, args, extraOptions }, offline: { effect: {}, commit: {}, rollback: {} } // 占位,阻止Redux Offline自动发送请求 } })) // 返回pending状态维持mutation加载态 return new Promise(() => {}) } // 在线状态正常发起请求 return baseQuery(args, api, extraOptions) } export const api = createApi({ baseQuery: offlineAwareBaseQuery, endpoints: (builder) => ({ registerUser: builder.mutation({ query: (userData) => ({ url: '/register', method: 'POST', body: new URLSearchParams(userData).toString(), headers: { 'content-type': 'application/x-www-form-urlencoded' } }), // 乐观更新逻辑与普通RTK Query写法完全一致 async onQueryStarted(userData, { dispatch, queryFulfilled }) { const patchResult = dispatch( api.util.updateQueryData('getUserList', undefined, (draft) => { draft.push(userData) }) ) try { await queryFulfilled } catch { patchResult.undo() } } }) }) }) export const { useRegisterUserMutation } = api
联网后自动重放队列逻辑
// store初始化后添加全局监听 let prevOnlineState = store.getState().offline.online store.subscribe(() => { const currentState = store.getState().offline // 检测到从离线切为在线时处理队列 if (!prevOnlineState && currentState.online && currentState.outbox.length > 0) { currentState.outbox.forEach(queueItem => { if (queueItem.type === 'RTK_QUERY_PENDING_MUTATION') { const { endpointName, args } = queueItem.meta.rtkQuery // 重发mutation store.dispatch(api.endpoints[endpointName].initiate(args)) // 移除已处理的队列项 store.dispatch(offlineActions.outbox.dequeue(queueItem)) } }) } prevOnlineState = currentState.online })
业务层使用mutation时和普通RTK Query写法没有区别,不需要为每个请求单独编写offline meta配置,通用逻辑已经封装在baseQuery和全局监听中。
内容的提问来源于stack exchange,提问作者cseelus
相关产品推荐
相关产品推荐

