or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-data.mdcore-components.mdcustomization.mdhooks-state.mdindex.mdtypes-interfaces.md

hooks-state.mddocs/

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

```