Skip to content

Commit ca2a603

Browse files
committed
refactor: revert useReducer, keep useState with persistence
Restore original useState pattern for filters/sort/pageSize to minimize diff from main. Persistence read/write is the only addition.
1 parent 5405181 commit ca2a603

2 files changed

Lines changed: 73 additions & 119 deletions

File tree

packages/core/src/hooks/use-collection-variables.ts

Lines changed: 57 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect, useMemo, useReducer, useRef } from "react";
1+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
22
import type {
33
BuildQueryVariables,
44
CollectionControl,
@@ -16,80 +16,6 @@ import type {
1616
} from "@/types/collection";
1717
import { useCursorPagination } from "./use-cursor-pagination";
1818

19-
// -----------------------------------------------------------------------------
20-
// Reducer
21-
// -----------------------------------------------------------------------------
22-
23-
interface CollectionState {
24-
filters: Filter[];
25-
sortStates: SortState[];
26-
pageSize: number;
27-
}
28-
29-
type CollectionAction =
30-
| {
31-
type: "ADD_FILTER";
32-
field: string;
33-
operator: string;
34-
value: unknown;
35-
caseSensitive?: boolean;
36-
}
37-
| { type: "SET_FILTERS"; filters: Filter[] }
38-
| { type: "REMOVE_FILTER"; field: string }
39-
| { type: "CLEAR_FILTERS" }
40-
| { type: "SET_SORT"; field: string; direction?: "Asc" | "Desc" }
41-
| { type: "CLEAR_SORT" }
42-
| { type: "SET_PAGE_SIZE"; pageSize: number };
43-
44-
function collectionReducer(state: CollectionState, action: CollectionAction): CollectionState {
45-
switch (action.type) {
46-
case "ADD_FILTER": {
47-
const { field, operator, value, caseSensitive } = action;
48-
const newFilter: Filter = {
49-
field,
50-
operator: operator as Filter["operator"],
51-
value,
52-
caseSensitive,
53-
};
54-
const existing = state.filters.findIndex((f) => f.field === field);
55-
const filters =
56-
existing >= 0
57-
? state.filters.map((f, i) => (i === existing ? newFilter : f))
58-
: [...state.filters, newFilter];
59-
return { ...state, filters };
60-
}
61-
case "SET_FILTERS":
62-
return { ...state, filters: action.filters };
63-
case "REMOVE_FILTER":
64-
return {
65-
...state,
66-
filters: state.filters.filter((f) => f.field !== action.field),
67-
};
68-
case "CLEAR_FILTERS":
69-
return { ...state, filters: [] };
70-
case "SET_SORT": {
71-
const { field, direction } = action;
72-
if (direction === undefined) {
73-
return {
74-
...state,
75-
sortStates: state.sortStates.filter((s) => s.field !== field),
76-
};
77-
}
78-
const filtered = state.sortStates.filter((s) => s.field !== field);
79-
return {
80-
...state,
81-
sortStates: [...filtered, { field, direction }],
82-
};
83-
}
84-
case "CLEAR_SORT":
85-
return { ...state, sortStates: [] };
86-
case "SET_PAGE_SIZE":
87-
return { ...state, pageSize: action.pageSize };
88-
default:
89-
return state;
90-
}
91-
}
92-
9319
// -----------------------------------------------------------------------------
9420
// Case-insensitive regex conversion helpers
9521
// -----------------------------------------------------------------------------
@@ -208,52 +134,48 @@ export function useCollectionVariables(
208134
const { initialFilters = [], initialSort = [], pageSize: initialPageSize = 20 } = params;
209135

210136
// ---------------------------------------------------------------------------
211-
// State (reducer-based)
137+
// Persistence read (once on mount)
212138
// ---------------------------------------------------------------------------
213-
const [state, dispatch] = useReducer(collectionReducer, undefined, () => {
214-
const snapshot = persistence?.read();
215-
return {
216-
filters: snapshot?.filters ?? initialFilters,
217-
sortStates: snapshot?.sort ?? initialSort,
218-
pageSize: snapshot?.pageSize ?? initialPageSize,
219-
};
220-
});
221-
222-
const { filters, sortStates, pageSize } = state;
139+
const [snapshot] = useState(() => persistence?.read());
223140

224141
// ---------------------------------------------------------------------------
225-
// Persistence write-back (skip initial render)
142+
// State
226143
// ---------------------------------------------------------------------------
227-
const isFirstRenderRef = useRef(true);
228-
useEffect(() => {
229-
if (isFirstRenderRef.current) {
230-
isFirstRenderRef.current = false;
231-
return;
232-
}
233-
persistence?.write({
234-
filters: state.filters,
235-
sort: state.sortStates,
236-
pageSize: state.pageSize,
237-
});
238-
}, [state.filters, state.sortStates, state.pageSize, persistence]);
144+
const [filters, setFiltersState] = useState<Filter[]>(snapshot?.filters ?? initialFilters);
145+
const [sortStates, setSortStates] = useState<SortState[]>(snapshot?.sort ?? initialSort);
239146

240-
// ---------------------------------------------------------------------------
241-
// Cursor pagination (pageSize owned by reducer, passed in)
242-
// ---------------------------------------------------------------------------
243147
const {
148+
pageSize,
244149
paginationVariables,
245150
goToNextPage,
246151
goToPrevPage,
247152
resetPage,
248153
goToFirstPage,
249154
goToLastPage,
155+
setPageSize: setCursorPageSize,
250156
getHasPrevPage,
251157
getHasNextPage,
252158
resetCount,
253-
} = useCursorPagination(pageSize);
159+
} = useCursorPagination(snapshot?.pageSize ?? initialPageSize);
254160

255161
// ---------------------------------------------------------------------------
256-
// Control actions (dispatch user actions + reset pagination)
162+
// Persistence write-back (skip initial render)
163+
// ---------------------------------------------------------------------------
164+
const isFirstRenderRef = useRef(true);
165+
useEffect(() => {
166+
if (isFirstRenderRef.current) {
167+
isFirstRenderRef.current = false;
168+
return;
169+
}
170+
persistence?.write({
171+
filters,
172+
sort: sortStates,
173+
pageSize,
174+
});
175+
}, [filters, sortStates, pageSize, persistence]);
176+
177+
// ---------------------------------------------------------------------------
178+
// Filter operations
257179
// ---------------------------------------------------------------------------
258180
const addFilter = useCallback(
259181
(
@@ -262,12 +184,20 @@ export function useCollectionVariables(
262184
value: unknown,
263185
filterOptions?: { caseSensitive?: boolean },
264186
) => {
265-
dispatch({
266-
type: "ADD_FILTER",
267-
field,
268-
operator,
269-
value,
270-
caseSensitive: filterOptions?.caseSensitive,
187+
setFiltersState((prev) => {
188+
const existing = prev.findIndex((f) => f.field === field);
189+
const newFilter: Filter = {
190+
field,
191+
operator,
192+
value,
193+
caseSensitive: filterOptions?.caseSensitive,
194+
};
195+
if (existing >= 0) {
196+
const updated = [...prev];
197+
updated[existing] = newFilter;
198+
return updated;
199+
}
200+
return [...prev, newFilter];
271201
});
272202
resetPage();
273203
},
@@ -276,44 +206,53 @@ export function useCollectionVariables(
276206

277207
const setFilters = useCallback(
278208
(newFilters: Filter[]) => {
279-
dispatch({ type: "SET_FILTERS", filters: newFilters });
209+
setFiltersState(newFilters);
280210
resetPage();
281211
},
282212
[resetPage],
283213
);
284214

285215
const removeFilter = useCallback(
286216
(field: string) => {
287-
dispatch({ type: "REMOVE_FILTER", field });
217+
setFiltersState((prev) => prev.filter((f) => f.field !== field));
288218
resetPage();
289219
},
290220
[resetPage],
291221
);
292222

293223
const clearFilters = useCallback(() => {
294-
dispatch({ type: "CLEAR_FILTERS" });
224+
setFiltersState([]);
295225
resetPage();
296226
}, [resetPage]);
297227

228+
// ---------------------------------------------------------------------------
229+
// Sort operations
230+
// ---------------------------------------------------------------------------
298231
const setSort = useCallback(
299232
(field: string, direction?: "Asc" | "Desc") => {
300-
dispatch({ type: "SET_SORT", field, direction });
233+
setSortStates((prev) => {
234+
if (direction === undefined) {
235+
return prev.filter((s) => s.field !== field);
236+
}
237+
const newState: SortState = { field, direction };
238+
const filtered = prev.filter((s) => s.field !== field);
239+
return [...filtered, newState];
240+
});
301241
resetPage();
302242
},
303243
[resetPage],
304244
);
305245

306246
const clearSort = useCallback(() => {
307-
dispatch({ type: "CLEAR_SORT" });
247+
setSortStates([]);
308248
resetPage();
309249
}, [resetPage]);
310250

311251
const setPageSize = useCallback(
312252
(size: number) => {
313-
dispatch({ type: "SET_PAGE_SIZE", pageSize: size });
314-
resetPage();
253+
setCursorPageSize(size);
315254
},
316-
[resetPage],
255+
[setCursorPageSize],
317256
);
318257

319258
// ---------------------------------------------------------------------------

packages/core/src/hooks/use-cursor-pagination.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ export interface UseCursorPaginationReturn {
8282
cursorStack: string[];
8383
/** Current fetch direction — determines whether `first`/`after` or `last`/`before` is sent. */
8484
paginationDirection: "forward" | "backward";
85+
/** Current page size. */
86+
pageSize: number;
8587
/** Ready-to-spread pagination variables for the GraphQL query. */
8688
paginationVariables: PaginationVariables;
8789
/**
@@ -114,6 +116,8 @@ export interface UseCursorPaginationReturn {
114116
* back to a full `pageSize` fetch.
115117
*/
116118
goToLastPage: (total?: number | null) => void;
119+
/** Change the page size and reset to the first page. */
120+
setPageSize: (size: number) => void;
117121
/**
118122
* Determine whether a previous page exists, given the server's `pageInfo`.
119123
*
@@ -153,7 +157,8 @@ export interface UseCursorPaginationReturn {
153157
*
154158
* @see {@link file://./use-collection-variables.ts useCollectionVariables} — the primary consumer.
155159
*/
156-
export function useCursorPagination(pageSize: number): UseCursorPaginationReturn {
160+
export function useCursorPagination(initialPageSize: number): UseCursorPaginationReturn {
161+
const [pageSize, setPageSizeState] = useState(initialPageSize);
157162
const [cursor, setCursor] = useState<string | null>(null);
158163
const [cursorStack, setCursorStack] = useState<string[]>([]);
159164
const [paginationDirection, setPaginationDirection] = useState<"forward" | "backward">("forward");
@@ -253,6 +258,14 @@ export function useCursorPagination(pageSize: number): UseCursorPaginationReturn
253258
[pageSize],
254259
);
255260

261+
const setPageSize = useCallback((size: number) => {
262+
setPageSizeState(size);
263+
setCursor(null);
264+
setCursorStack([]);
265+
setPaginationDirection("forward");
266+
setLastPageSize(null);
267+
}, []);
268+
256269
// ---------------------------------------------------------------------------
257270
// Computed pagination variables
258271
// ---------------------------------------------------------------------------
@@ -290,12 +303,14 @@ export function useCursorPagination(pageSize: number): UseCursorPaginationReturn
290303
cursor,
291304
cursorStack,
292305
paginationDirection,
306+
pageSize,
293307
paginationVariables,
294308
goToNextPage,
295309
goToPrevPage,
296310
resetPage: resetPagination,
297311
goToFirstPage,
298312
goToLastPage,
313+
setPageSize,
299314
getHasPrevPage,
300315
getHasNextPage,
301316
resetCount,

0 commit comments

Comments
 (0)