0
# Structured Selectors
1
2
Utility for creating selectors that return objects with the same keys as the input selectors object, where each value is computed by the corresponding selector.
3
4
## Capabilities
5
6
### createStructuredSelector
7
8
Creates a selector that returns an object with the same shape as the input selectors object, but with values computed by running each selector.
9
10
```typescript { .api }
11
/**
12
* Creates a structured selector from an object of selectors
13
* @param selectors - Object where each property is a selector function
14
* @returns OutputSelector that returns object with same keys but computed values
15
*/
16
function createStructuredSelector<TSelectors extends SelectorsObject>(
17
selectors: TSelectors
18
): OutputSelector<
19
GetStateFromSelectors<TSelectors>,
20
SelectorResultsMap<TSelectors>
21
>;
22
23
/**
24
* Creates a structured selector with a custom selector creator
25
* @param selectors - Object where each property is a selector function
26
* @param selectorCreator - Custom createSelector function to use
27
* @returns OutputSelector using the custom selector creator
28
*/
29
function createStructuredSelector<TSelectors extends SelectorsObject>(
30
selectors: TSelectors,
31
selectorCreator: CreateSelectorFunction
32
): OutputSelector<
33
GetStateFromSelectors<TSelectors>,
34
SelectorResultsMap<TSelectors>
35
>;
36
```
37
38
**Basic Usage:**
39
40
```typescript
41
import { createStructuredSelector } from "reselect";
42
43
// Individual selectors
44
const selectItems = (state) => state.items;
45
const selectLoading = (state) => state.loading;
46
const selectError = (state) => state.error;
47
const selectUser = (state) => state.user;
48
49
// Create structured selector
50
const selectAppData = createStructuredSelector({
51
items: selectItems,
52
loading: selectLoading,
53
error: selectError,
54
user: selectUser
55
});
56
57
// Usage
58
const appData = selectAppData(state);
59
// Result: { items: [...], loading: false, error: null, user: {...} }
60
```
61
62
**With Custom Selector Creator:**
63
64
```typescript
65
import { createStructuredSelector, createSelectorCreator, lruMemoize } from "reselect";
66
67
// Custom selector creator with LRU memoization
68
const createLRUSelector = createSelectorCreator({
69
memoize: lruMemoize,
70
memoizeOptions: { maxSize: 10 }
71
});
72
73
// Structured selector using custom creator
74
const selectDashboardData = createStructuredSelector({
75
stats: selectStats,
76
recentActivity: selectRecentActivity,
77
notifications: selectNotifications
78
}, createLRUSelector);
79
```
80
81
**Nested Structured Selectors:**
82
83
```typescript
84
import { createStructuredSelector } from "reselect";
85
86
// Create nested structure
87
const selectUserProfile = createStructuredSelector({
88
personal: createStructuredSelector({
89
name: (state) => state.user.name,
90
email: (state) => state.user.email,
91
avatar: (state) => state.user.avatar
92
}),
93
preferences: createStructuredSelector({
94
theme: (state) => state.user.preferences.theme,
95
language: (state) => state.user.preferences.language,
96
notifications: (state) => state.user.preferences.notifications
97
}),
98
stats: createStructuredSelector({
99
loginCount: (state) => state.user.stats.loginCount,
100
lastLogin: (state) => state.user.stats.lastLogin
101
})
102
});
103
104
// Result has nested structure:
105
// {
106
// personal: { name: "...", email: "...", avatar: "..." },
107
// preferences: { theme: "...", language: "...", notifications: ... },
108
// stats: { loginCount: 42, lastLogin: "..." }
109
// }
110
```
111
112
### Root State Selectors
113
114
Creating selectors for each key in a root state object.
115
116
```typescript
117
import { createStructuredSelector } from "reselect";
118
119
// For a state shape like: { todos: [...], users: [...], settings: {...} }
120
const selectAllSlices = createStructuredSelector({
121
todos: (state) => state.todos,
122
users: (state) => state.users,
123
settings: (state) => state.settings
124
});
125
126
// Equivalent to manually writing:
127
const selectAllSlicesManual = createSelector(
128
[(state) => state.todos, (state) => state.users, (state) => state.settings],
129
(todos, users, settings) => ({ todos, users, settings })
130
);
131
```
132
133
### TypeScript Usage with Typed State
134
135
```typescript
136
interface RootState {
137
users: User[];
138
posts: Post[];
139
ui: {
140
loading: boolean;
141
error: string | null;
142
};
143
}
144
145
interface User {
146
id: number;
147
name: string;
148
email: string;
149
}
150
151
interface Post {
152
id: number;
153
title: string;
154
authorId: number;
155
}
156
157
// Typed structured selector
158
const selectPageData = createStructuredSelector({
159
users: (state: RootState) => state.users,
160
posts: (state: RootState) => state.posts,
161
loading: (state: RootState) => state.ui.loading,
162
error: (state: RootState) => state.ui.error
163
});
164
165
// TypeScript infers the return type:
166
// {
167
// users: User[];
168
// posts: Post[];
169
// loading: boolean;
170
// error: string | null;
171
// }
172
```
173
174
### Advanced Patterns
175
176
**Computed Properties in Structured Selectors:**
177
178
```typescript
179
import { createStructuredSelector, createSelector } from "reselect";
180
181
// Mix simple selectors with computed ones
182
const selectOrderSummary = createStructuredSelector({
183
// Simple selectors
184
items: (state) => state.cart.items,
185
customer: (state) => state.customer,
186
187
// Computed selectors
188
subtotal: createSelector(
189
[(state) => state.cart.items],
190
(items) => items.reduce((sum, item) => sum + item.price * item.quantity, 0)
191
),
192
193
tax: createSelector(
194
[(state) => state.cart.items, (state) => state.customer.taxRate],
195
(items, taxRate) => {
196
const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
197
return subtotal * taxRate;
198
}
199
),
200
201
total: createSelector(
202
[
203
(state) => state.cart.items,
204
(state) => state.customer.taxRate
205
],
206
(items, taxRate) => {
207
const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
208
const tax = subtotal * taxRate;
209
return subtotal + tax;
210
}
211
)
212
});
213
```
214
215
### Pre-Typed Structured Selectors
216
217
Create pre-typed versions of `createStructuredSelector` with predefined state types.
218
219
```typescript
220
import { createStructuredSelector } from "reselect";
221
222
interface RootState {
223
todos: { id: number; completed: boolean }[];
224
users: { id: number; name: string }[];
225
ui: { loading: boolean; error: string | null };
226
}
227
228
// Create a pre-typed createStructuredSelector
229
export const createStructuredAppSelector = createStructuredSelector.withTypes<RootState>();
230
231
// Now use it without specifying state types
232
const selectAppData = createStructuredAppSelector({
233
// Type of `state` is automatically set to `RootState`
234
todos: state => state.todos,
235
users: state => state.users,
236
loading: state => state.ui.loading,
237
error: state => state.ui.error
238
});
239
240
// TypeScript knows the return type:
241
// {
242
// todos: { id: number; completed: boolean }[];
243
// users: { id: number; name: string }[];
244
// loading: boolean;
245
// error: string | null;
246
// }
247
```
248
249
## Types
250
251
```typescript { .api }
252
type SelectorsObject = Record<string, Selector>;
253
254
type SelectorResultsMap<TObject extends SelectorsObject> = {
255
[Key in keyof TObject]: ReturnType<TObject[Key]>;
256
};
257
258
type RootStateSelectors<TState> = {
259
[Key in keyof TState]: Selector<TState, TState[Key]>;
260
};
261
262
type GetStateFromSelectors<TSelectors extends SelectorsObject> =
263
TSelectors[keyof TSelectors] extends Selector<infer State, any> ? State : never;
264
265
interface StructuredSelectorCreator {
266
<TSelectors extends SelectorsObject>(
267
selectors: TSelectors
268
): OutputSelector<GetStateFromSelectors<TSelectors>, SelectorResultsMap<TSelectors>>;
269
270
<TSelectors extends SelectorsObject>(
271
selectors: TSelectors,
272
selectorCreator: CreateSelectorFunction
273
): OutputSelector<GetStateFromSelectors<TSelectors>, SelectorResultsMap<TSelectors>>;
274
275
withTypes: <OverrideStateType>() => StructuredSelectorCreator<OverrideStateType>;
276
}
277
278
type TypedStructuredSelectorCreator<State> = <TSelectors extends RootStateSelectors<State>>(
279
selectors: TSelectors
280
) => OutputSelector<State, SelectorResultsMap<TSelectors>>;
281
```