ngrx/entity pagination selector: configuring page‑size validation
0 reputation · 11 Nov 2025, 09:18 UTC
0 reputation · 11 Nov 2025, 09:18 UTC
Goal: ensure that the pagination selector created with createPaginationSelector validates that the supplied page size is a positive integer before slicing the entity map.
Currently the selector accepts any number; zero or negative produce an empty slice, and excessively large page sizes cause unnecessary work. This silent behavior can hide bugs in UI components that miscalculate pagination parameters, especially when combined with rapid scrolling effects.
Uncertainty: should the selector throw an error, automatically clamp to a minimum of 1, or leave validation to consumer code? Need to decide on a configuration option or default behavior that balances safety and flexibility.
Should the selector throw a TypeError when pageSize ≤ 0? Should it automatically reset pageSize to 1 in such cases? Should the library expose a validator function that callers can invoke before selecting?
34775 reputation · 11 Nov 2025, 20:11 UTC
To prevent silent failures or performance degradation caused by invalid pagination parameters, you should implement a clamping mechanism within your selector. In NgRx, selectors must remain pure functions; therefore, throwing a TypeError is discouraged as it would crash the selector chain and potentially the entire application UI.
The most robust approach is to automatically clamp the pageSize to a safe range (e.g., between 1 and a defined maximum) before the slicing operation occurs. This ensures the selector always returns a valid array without triggering side effects.
import { createSelector } from '@ngrx/store';
const MIN_PAGE_SIZE = 1;
const MAX_PAGE_SIZE = 100;
export const selectPaginatedEntities = createSelector(
selectEntityArray, // The full list of entities
selectPaginationState, // { pageIndex: number, pageSize: number }
(entities, pagination) => {
// Validate and clamp the page size
const safePageSize = Math.max(
MIN_PAGE_SIZE,
Math.min(MAX_PAGE_SIZE, pagination.pageSize)
);
const start = pagination.pageIndex * safePageSize;
return entities.slice(start, start + safePageSize);
}
);
| Strategy | Behavior | Verdict |
|---|---|---|
| Throw TypeError | Crashes the selector chain on invalid input. | Avoid: Too disruptive for UI state. |
| Automatic Clamping | Resets $\le 0$ to 1 and caps excessively large values. | Recommended: Safe, predictable, and pure. |
| External Validator | Validates in the component/effect before updating state. | Supplementary: Good for UX, but doesn't protect the selector. |
pageSize to 0 or -1. Verify the selector returns a slice of 1 element (or the MIN_PAGE_SIZE).pageSize to 10,000. Verify the selector caps the result at MAX_PAGE_SIZE.Diagnostic Note: Are you currently calculating the pageIndex based on a total count in the selector, or is the index managed independently in the state? If the index is managed independently, you may also need to clamp the start offset to prevent it from exceeding the total array length.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 11 Nov 2025, 17:32 UTC
The entity adapter state holds only ids and entities. Pagination meta such as page and pageSize lives alongside it in the feature slice, and selectors combine both parts.
Selectors must stay pure, so throwing or mutating on a bad pageSize breaks memoization and hides where the invalid input originated. Normalizing the request in the reducer or effect, for example clamping to MIN_PAGE_SIZE and MAX_PAGE_SIZE and resetting page to 1 when pageSize changes, keeps the selector a simple derived view: selectAll then slice by offset.
That keeps the selector testable and makes invalid inputs visible in the action stream.