Created
July 10, 2025 12:15
-
-
Save lindskogen/ba21f1707d0c0cf90055ae4e24b8eeba to your computer and use it in GitHub Desktop.
offlineRTK
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { isAnyOf, isFulfilled, createAction, createReducer, Middleware, UnknownAction } from '@reduxjs/toolkit'; | |
interface PendingAction { | |
meta: { | |
requestId: string; | |
requestStatus: 'pending'; | |
startedTimeStamp: number; | |
}; | |
type: string; | |
payload: unknown; | |
} | |
interface OfflineQueueState { | |
queue: string[]; | |
meta: Record<string, PendingAction>; | |
} | |
const initialState: OfflineQueueState = { | |
queue: [], | |
meta: {} | |
}; | |
export const pushOfflineQueueAction = createAction<PendingAction>('offline/queue/push'); | |
export const offlineQueueReducer = createReducer(initialState, builder => { | |
builder | |
.addCase(pushOfflineQueueAction, (state, action) => { | |
const innerAction = action.payload; | |
state.queue.push(innerAction.meta.requestId); | |
state.meta[innerAction.meta.requestId] = innerAction; | |
return state; | |
}) | |
.addMatcher(isFulfilled, (state, action) => { | |
if (action.meta.requestId in state.meta) { | |
delete state.meta[action.meta.requestId]; | |
} | |
}); | |
}); | |
const isPendingApiAction = isAnyOf(api.endpoints.updateUser.matchPending); | |
export const offlineQueueMiddleware: Middleware = | |
({ getState, dispatch }) => | |
next => | |
action => { | |
if (isPendingApiAction(action)) { | |
const isOffline = selectIsOffline(getState()); | |
if (isOffline) { | |
// we're offline, abort and add to queue! | |
dispatch(pushOfflineQueueAction(action)); | |
// Attempt 1: | |
// Don't call next() | |
// still runs the thunk, just skips dispatching the pending action | |
// Attempt 2: | |
// Dispatch action with same structure as when calling action.abort() | |
// still runs the thunk, no change. | |
// Attempt 3: | |
// Get the thunk with `getRunningMutationThunk`: | |
// | |
// const thunk = dispatch( | |
// api.util.getRunningMutationThunk(action.meta.arg.endpointName as any, action.meta.requestId) | |
// ); | |
// thunk.abort(); | |
// | |
// It doesn't find it? I guess it's not started yet? | |
// Attempt 4: | |
// Throw error | |
// the thunk gets cancelled! | |
// throw new Error('Offline error'); | |
} else { | |
console.log('is online, let the action through!', action); | |
return next(action); | |
} | |
} else { | |
return next(action); | |
} | |
}; | |
export const addOfflineQueueListener = (startListening: AppStartListening) => { | |
startListening({ | |
actionCreator: online, | |
effect: async (_onlineAction, { getState, dispatch }) => { | |
const { queue, meta } = getState().offlineQueue; | |
for (const requestId of queue) { | |
// but this does not actually re-start the thunk - it's just the pending action | |
await dispatch(meta[requestId]); | |
} | |
} | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment