0
# Hooks and State Management
1
2
React-Select provides powerful hooks for advanced component integration and custom state management. These hooks allow you to build custom select implementations while leveraging React-Select's core functionality.
3
4
## Capabilities
5
6
### useStateManager Hook
7
8
Core state management hook that provides the same functionality as the main Select component but with full control over rendering.
9
10
```typescript { .api }
11
/**
12
* State management hook providing full control over select state and behavior
13
* @param props - State manager props with default values and event handlers
14
* @returns Complete state object and methods for managing select behavior
15
*/
16
function useStateManager<
17
Option = unknown,
18
IsMulti extends boolean = false,
19
Group extends GroupBase<Option> = GroupBase<Option>
20
>(props: StateManagerProps<Option, IsMulti, Group>): StateManagerReturn<Option, IsMulti, Group>;
21
22
interface StateManagerProps<Option, IsMulti extends boolean, Group extends GroupBase<Option>> {
23
/** Default input value */
24
defaultInputValue?: string;
25
/** Default menu open state */
26
defaultMenuIsOpen?: boolean;
27
/** Default selected value(s) */
28
defaultValue?: PropsValue<Option>;
29
/** Input value for controlled input */
30
inputValue?: string;
31
/** Menu open state for controlled menu */
32
menuIsOpen?: boolean;
33
/** Selected value(s) for controlled component */
34
value?: PropsValue<Option>;
35
/** Change handler for value updates */
36
onChange?: (newValue: OnChangeValue<Option, IsMulti>, actionMeta: ActionMeta<Option>) => void;
37
/** Input change handler */
38
onInputChange?: (newValue: string, actionMeta: InputActionMeta) => void;
39
/** Menu open handler */
40
onMenuOpen?: () => void;
41
/** Menu close handler */
42
onMenuClose?: () => void;
43
}
44
45
interface StateManagerReturn<Option, IsMulti extends boolean, Group extends GroupBase<Option>> {
46
/** Current input value */
47
inputValue: string;
48
/** Current menu open state */
49
menuIsOpen: boolean;
50
/** Current selected value(s) */
51
value: PropsValue<Option>;
52
/** Set input value */
53
setValue: (newValue: PropsValue<Option>, actionMeta: ActionMeta<Option>) => void;
54
/** Set input text */
55
setInputValue: (newValue: string, actionMeta: InputActionMeta) => void;
56
/** Set menu open state */
57
setMenuIsOpen: (newValue: boolean, actionMeta: ActionMeta<Option>) => void;
58
}
59
```
60
61
**Usage Examples:**
62
63
```typescript
64
import { useStateManager } from "react-select";
65
66
// Custom select with external state control
67
const CustomSelect = () => {
68
const {
69
inputValue,
70
menuIsOpen,
71
value,
72
setValue,
73
setInputValue,
74
setMenuIsOpen
75
} = useStateManager({
76
defaultValue: null,
77
defaultInputValue: "",
78
defaultMenuIsOpen: false,
79
onChange: (newValue, actionMeta) => {
80
console.log("Value changed:", newValue, actionMeta);
81
setValue(newValue, actionMeta);
82
}
83
});
84
85
// Custom rendering logic using state
86
return (
87
<div>
88
<input
89
value={inputValue}
90
onChange={(e) => setInputValue(e.target.value, { action: 'input-change' })}
91
/>
92
<button onClick={() => setMenuIsOpen(!menuIsOpen)}>
93
{menuIsOpen ? 'Close' : 'Open'} Menu
94
</button>
95
{/* Custom menu rendering based on state */}
96
</div>
97
);
98
};
99
```
100
101
### useAsync Hook
102
103
Hook for implementing async option loading functionality in custom select components.
104
105
```typescript { .api }
106
/**
107
* Async data loading hook for dynamic option fetching
108
* @param props - Async configuration with load function and caching options
109
* @returns Async state and methods for loading options
110
*/
111
function useAsync<Option = unknown>(props: AsyncProps<Option>): AsyncState<Option>;
112
113
interface AsyncProps<Option> {
114
/** Default options to show before any input */
115
defaultOptions?: OptionsOrGroups<Option, Group> | boolean;
116
/** Enable caching of loaded options */
117
cacheOptions?: any;
118
/** Function to load options based on input value */
119
loadOptions?: (
120
inputValue: string,
121
callback: (options: OptionsOrGroups<Option, Group>) => void
122
) => Promise<OptionsOrGroups<Option, Group>> | void;
123
}
124
125
interface AsyncState<Option> {
126
/** Currently loaded options */
127
options: OptionsOrGroups<Option, Group>;
128
/** Loading state indicator */
129
isLoading: boolean;
130
/** Error state from failed loads */
131
error: Error | null;
132
/** Function to trigger option loading */
133
loadOptions: (inputValue: string) => void;
134
/** Function to clear loaded options */
135
clearOptions: () => void;
136
}
137
```
138
139
**Usage Examples:**
140
141
```typescript
142
import { useAsync } from "react-select";
143
144
// Custom async select implementation
145
const CustomAsyncSelect = () => {
146
const {
147
options,
148
isLoading,
149
error,
150
loadOptions
151
} = useAsync({
152
defaultOptions: true,
153
cacheOptions: true,
154
loadOptions: async (inputValue: string) => {
155
const response = await fetch(`/api/search?q=${inputValue}`);
156
const data = await response.json();
157
return data.map((item: any) => ({
158
value: item.id,
159
label: item.name
160
}));
161
}
162
});
163
164
return (
165
<div>
166
<input
167
onChange={(e) => loadOptions(e.target.value)}
168
placeholder="Type to search..."
169
/>
170
{isLoading && <div>Loading...</div>}
171
{error && <div>Error: {error.message}</div>}
172
<ul>
173
{options.map((option) => (
174
<li key={option.value}>{option.label}</li>
175
))}
176
</ul>
177
</div>
178
);
179
};
180
```
181
182
### useCreatable Hook
183
184
Hook for implementing creatable option functionality in custom select components.
185
186
```typescript { .api }
187
/**
188
* Creatable options hook for dynamic option creation
189
* @param props - Creatable configuration with creation handlers
190
* @returns Creatable state and methods for managing option creation
191
*/
192
function useCreatable<Option = unknown>(props: CreatableProps<Option>): CreatableState<Option>;
193
194
interface CreatableProps<Option> {
195
/** Allow creating new options */
196
allowCreateWhileLoading?: boolean;
197
/** Handler for creating new options */
198
onCreateOption?: (inputValue: string) => void;
199
/** Function to determine if new option can be created */
200
isValidNewOption?: (inputValue: string, selectValue: Options<Option>, selectOptions: OptionsOrGroups<Option, Group>) => boolean;
201
/** Function to get new option data */
202
getNewOptionData?: (inputValue: string, optionLabel: ReactNode) => Option;
203
/** Function to format create option label */
204
formatCreateLabel?: (inputValue: string) => ReactNode;
205
/** Function to determine if option was created by user */
206
isOptionDisabled?: (option: Option, selectValue: Options<Option>) => boolean;
207
}
208
209
interface CreatableState<Option> {
210
/** Current options including created ones */
211
options: OptionsOrGroups<Option, Group>;
212
/** Whether a new option can be created for current input */
213
canCreateOption: boolean;
214
/** Function to create new option */
215
createOption: (inputValue: string) => void;
216
/** Function to check if option is newly created */
217
isNewOption: (option: Option) => boolean;
218
}
219
```
220
221
**Usage Examples:**
222
223
```typescript
224
import { useCreatable } from "react-select";
225
226
// Custom creatable select implementation
227
const CustomCreatableSelect = () => {
228
const [options, setOptions] = useState([
229
{ value: "chocolate", label: "Chocolate" },
230
{ value: "strawberry", label: "Strawberry" }
231
]);
232
233
const {
234
canCreateOption,
235
createOption,
236
isNewOption
237
} = useCreatable({
238
onCreateOption: (inputValue: string) => {
239
const newOption = {
240
value: inputValue.toLowerCase(),
241
label: inputValue,
242
__isNew__: true
243
};
244
setOptions([...options, newOption]);
245
},
246
isValidNewOption: (inputValue, selectValue, selectOptions) => {
247
return inputValue.length > 0 &&
248
!selectOptions.some(option => option.label.toLowerCase() === inputValue.toLowerCase());
249
},
250
formatCreateLabel: (inputValue) => `Create "${inputValue}"`
251
});
252
253
return (
254
<div>
255
{/* Custom implementation using creatable state */}
256
</div>
257
);
258
};
259
```
260
261
## State Management Patterns
262
263
### Controlled vs Uncontrolled Components
264
265
React-Select supports both controlled and uncontrolled patterns through the state manager hooks.
266
267
**Controlled Pattern:**
268
```typescript
269
// Fully controlled select with external state
270
const ControlledSelect = () => {
271
const [value, setValue] = useState(null);
272
const [inputValue, setInputValue] = useState("");
273
const [menuIsOpen, setMenuIsOpen] = useState(false);
274
275
const stateManager = useStateManager({
276
value,
277
inputValue,
278
menuIsOpen,
279
onChange: setValue,
280
onInputChange: setInputValue,
281
onMenuOpen: () => setMenuIsOpen(true),
282
onMenuClose: () => setMenuIsOpen(false)
283
});
284
285
// Use stateManager for rendering
286
};
287
```
288
289
**Uncontrolled Pattern:**
290
```typescript
291
// Uncontrolled select with default values
292
const UncontrolledSelect = () => {
293
const stateManager = useStateManager({
294
defaultValue: { value: "default", label: "Default Option" },
295
defaultInputValue: "",
296
defaultMenuIsOpen: false,
297
onChange: (value, actionMeta) => {
298
// Handle changes without controlling state
299
console.log("Selection changed:", value);
300
}
301
});
302
303
// Use stateManager for rendering
304
};
305
```
306
307
### Integration with External State Management
308
309
React-Select hooks integrate seamlessly with Redux, Zustand, or other state management libraries.
310
311
```typescript
312
// Redux integration example
313
const ReduxSelect = () => {
314
const dispatch = useDispatch();
315
const selectState = useSelector(state => state.select);
316
317
const stateManager = useStateManager({
318
value: selectState.value,
319
inputValue: selectState.inputValue,
320
menuIsOpen: selectState.menuIsOpen,
321
onChange: (value, actionMeta) => {
322
dispatch(updateSelectValue({ value, actionMeta }));
323
},
324
onInputChange: (inputValue, actionMeta) => {
325
dispatch(updateInputValue({ inputValue, actionMeta }));
326
}
327
});
328
329
// Render using state manager
330
};
331
```
332
333
## Types
334
335
### StateManagerProps
336
337
Complete props interface for useStateManager hook.
338
339
```typescript { .api }
340
interface StateManagerProps<Option, IsMulti extends boolean, Group extends GroupBase<Option>>
341
extends Omit<PublicBaseSelectProps<Option, IsMulti, Group>, StateManagedPropKeys> {
342
defaultInputValue?: string;
343
defaultMenuIsOpen?: boolean;
344
defaultValue?: PropsValue<Option>;
345
}
346
347
type StateManagedPropKeys =
348
| 'inputValue'
349
| 'menuIsOpen'
350
| 'onChange'
351
| 'onInputChange'
352
| 'onMenuClose'
353
| 'onMenuOpen'
354
| 'value';
355
```
356
357
### AsyncState and CreatableState
358
359
State interfaces returned by async and creatable hooks.
360
361
```typescript { .api }
362
interface AsyncState<Option> {
363
options: OptionsOrGroups<Option, Group>;
364
isLoading: boolean;
365
error: Error | null;
366
loadOptions: (inputValue: string) => void;
367
clearOptions: () => void;
368
}
369
370
interface CreatableState<Option> {
371
options: OptionsOrGroups<Option, Group>;
372
canCreateOption: boolean;
373
createOption: (inputValue: string) => void;
374
isNewOption: (option: Option) => boolean;
375
}
376
```