Apply only the latest connectors load

Overlapping loadConnectors calls applied whichever answer arrived last,
so a load started before a connect could replace the newer connection
list. The slice now records the latest request and ignores older
answers.
This commit is contained in:
arc53-machine committed 2026-09-29 15:11:40 +01:00
1 parent c958b68191
commit 4b87f97785
2 files changed
+73 -2

No files matched your search

@@ -0,0 +1,59 @@
import reducer, {
loadConnectors,
type ConnectorsState,
} from './connectorsSlice';
import type { Connection } from './types';
const arg = { token: null };
const connection = (id: string) => ({ id }) as unknown as Connection;
const payload = (ids: string[]) => ({
catalog: [],
connections: ids.map(connection),
});
const pending = (state: ConnectorsState | undefined, requestId: string) =>
reducer(state, loadConnectors.pending(requestId, arg));
describe('connectorsSlice loadConnectors', () => {
it('keeps the newer response when an older one arrives last', () => {
let state = pending(undefined, 'old');
state = pending(state, 'new');
state = reducer(
state,
loadConnectors.fulfilled(payload(['after-connect']), 'new', arg),
);
state = reducer(
state,
loadConnectors.fulfilled(payload(['before-connect']), 'old', arg),
);
expect(state.connections.map((c) => c.id)).toEqual(['after-connect']);
expect(state.loading).toBe(false);
});
it('stays loading while the latest request is still running', () => {
let state = pending(undefined, 'old');
state = pending(state, 'new');
state = reducer(
state,
loadConnectors.fulfilled(payload(['stale']), 'old', arg),
);
expect(state.loading).toBe(true);
expect(state.connections).toEqual([]);
});
it('ignores the failure of an older request', () => {
let state = pending(undefined, 'old');
state = pending(state, 'new');
state = reducer(
state,
loadConnectors.rejected(new Error('boom'), 'old', arg),
);
expect(state.failed).toBe(false);
state = reducer(
state,
loadConnectors.rejected(new Error('boom'), 'new', arg),
);
expect(state.failed).toBe(true);
expect(state.loading).toBe(false);
});
});
+14 -2
View File
@@ -15,6 +15,12 @@ export type ConnectorsState = {
loading: boolean;
loaded: boolean;
failed: boolean;
/**
* The `loadConnectors` call started last. Loads overlap (several screens
* refresh at once), and only its answer is applied: an older one could
* hold connections from before a connect.
*/
latestRequestId?: string | null;
};
const initialState: ConnectorsState = {
@@ -24,6 +30,7 @@ const initialState: ConnectorsState = {
loading: false,
loaded: false,
failed: false,
latestRequestId: null,
};
/**
@@ -58,17 +65,22 @@ const connectorsSlice = createSlice({
},
extraReducers: (builder) => {
builder
.addCase(loadConnectors.pending, (state) => {
.addCase(loadConnectors.pending, (state, action) => {
state.latestRequestId = action.meta.requestId;
state.loading = true;
state.failed = false;
})
.addCase(loadConnectors.fulfilled, (state, action) => {
if (action.meta.requestId !== state.latestRequestId) return;
state.latestRequestId = null;
state.loading = false;
state.loaded = true;
state.catalog = action.payload.catalog;
state.connections = action.payload.connections;
})
.addCase(loadConnectors.rejected, (state) => {
.addCase(loadConnectors.rejected, (state, action) => {
if (action.meta.requestId !== state.latestRequestId) return;
state.latestRequestId = null;
state.loading = false;
state.failed = true;
});