From harness-claude
Derives and memoizes computed Redux state using createSelector to prevent redundant calculations and unnecessary re-renders. For filtered lists, totals, and parameterized selectors.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Derive and memoize computed state with createSelector to avoid redundant calculations and unnecessary re-renders
Guides state selection patterns for Zustand, Redux, Jotai, and React Context to minimize component re-renders from over-subscription or unstable references.
Guides Redux and Redux Toolkit for global state management: slices, stores, actions, reducers, hooks, selectors, middleware, async thunks.
Generates Redux slices and operations for frontend state management. Auto-activates on phrases like 'redux slice generator', 'redux generator', 'redux'. Useful for React-based projects needing quick slice boilerplate.
Share bugs, ideas, or general feedback.
Derive and memoize computed state with createSelector to avoid redundant calculations and unnecessary re-renders
createSelector from @reduxjs/toolkit (re-exported from Reselect) for any computation that produces derived data.state.someSlice.nested.field from outside the slice boundary.// features/todos/todos.selectors.ts
import { createSelector } from '@reduxjs/toolkit';
import { RootState } from '../../store';
// Simple selectors (no memoization needed)
const selectTodosState = (state: RootState) => state.todos;
export const selectAllTodos = (state: RootState) => state.todos.items;
export const selectFilter = (state: RootState) => state.todos.filter;
// Memoized derived selector
export const selectFilteredTodos = createSelector(
[selectAllTodos, selectFilter],
(todos, filter) => {
switch (filter) {
case 'active':
return todos.filter((t) => !t.completed);
case 'completed':
return todos.filter((t) => t.completed);
default:
return todos;
}
}
);
// Composed selector
export const selectTodoStats = createSelector([selectAllTodos], (todos) => ({
total: todos.length,
completed: todos.filter((t) => t.completed).length,
active: todos.filter((t) => !t.completed).length,
}));
// Parameterized selector factory
export const makeSelectTodoById = (id: string) =>
createSelector([selectAllTodos], (todos) => todos.find((t) => t.id === id));
How memoization works: createSelector caches the last result. If all input selectors return the same references as last time, the result function is skipped and the cached result is returned. This is reference equality, not deep equality.
Common memoization mistakes:
createSelector inside a component — creates a new selector instance on every render, defeating memoization. Move selectors outside components or use useMemo.items.filter(...) from a simple selector (not createSelector) — creates a new array every time, causing re-renders.Per-component memoization: When multiple component instances need the same parameterized selector with different args, use a factory with useMemo:
function TodoItem({ id }: { id: string }) {
const selectTodo = useMemo(() => makeSelectTodoById(id), [id]);
const todo = useAppSelector(selectTodo);
// Each TodoItem has its own memoized selector
}
Reselect 5.0+ (RTK 2.0+): Supports createSelector with custom equality checks and weakMapMemoize for multi-argument caching without factories.
Testing selectors: Test them as pure functions — pass in a mock RootState and assert the output. They are the easiest Redux code to test.
https://redux-toolkit.js.org/api/createSelector