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

async-data.mddocs/

0

# Async Data Loading

1

2

React-Select provides powerful async data loading capabilities through AsyncSelect and AsyncCreatableSelect components, along with the useAsync hook for custom implementations. This enables dynamic option fetching, caching, and loading state management.

3

4

## Capabilities

5

6

### AsyncSelect Component

7

8

Select component with built-in async option loading, caching, and loading state management.

9

10

```typescript { .api }

11

/**

12

* Select component with async option loading capabilities

13

* @param props - Standard Props plus AsyncProps for async functionality

14

* @returns JSX.Element with async loading capabilities

15

*/

16

function AsyncSelect<

17

Option = unknown,

18

IsMulti extends boolean = false,

19

Group extends GroupBase<Option> = GroupBase<Option>

20

>(props: Props<Option, IsMulti, Group> & AsyncProps<Option>): JSX.Element;

21

22

interface AsyncProps<Option> {

23

/** Default options to show before any input, or true to load options immediately */

24

defaultOptions?: OptionsOrGroups<Option, Group> | boolean;

25

/** Enable caching of loaded options to avoid repeated API calls */

26

cacheOptions?: any;

27

/**

28

* Function to load options asynchronously based on input value

29

* Can return Promise or use callback pattern

30

*/

31

loadOptions?: (

32

inputValue: string,

33

callback: (options: OptionsOrGroups<Option, Group>) => void

34

) => Promise<OptionsOrGroups<Option, Group>> | void;

35

}

36

```

37

38

**Usage Examples:**

39

40

```typescript

41

import AsyncSelect from "react-select/async";

42

43

// Promise-based async loading

44

const PromiseAsyncSelect = () => {

45

const loadOptions = async (inputValue: string): Promise<Option[]> => {

46

const response = await fetch(`/api/search?q=${inputValue}`);

47

const data = await response.json();

48

return data.map((item: any) => ({

49

value: item.id,

50

label: item.name,

51

}));

52

};

53

54

return (

55

<AsyncSelect

56

cacheOptions

57

defaultOptions

58

loadOptions={loadOptions}

59

placeholder="Type to search..."

60

noOptionsMessage={({ inputValue }) =>

61

inputValue ? `No results for "${inputValue}"` : "Type to search"

62

}

63

/>

64

);

65

};

66

67

// Callback-based async loading

68

const CallbackAsyncSelect = () => {

69

const loadOptions = (inputValue: string, callback: Function) => {

70

if (!inputValue) {

71

callback([]);

72

return;

73

}

74

75

setTimeout(() => {

76

const filteredOptions = allOptions.filter((option) =>

77

option.label.toLowerCase().includes(inputValue.toLowerCase())

78

);

79

callback(filteredOptions);

80

}, 1000);

81

};

82

83

return (

84

<AsyncSelect

85

loadOptions={loadOptions}

86

placeholder="Type to search..."

87

cacheOptions

88

/>

89

);

90

};

91

92

// With error handling

93

const ErrorHandlingAsync = () => {

94

const [isLoading, setIsLoading] = useState(false);

95

const [error, setError] = useState<string | null>(null);

96

97

const loadOptions = async (inputValue: string) => {

98

if (!inputValue) return [];

99

100

setIsLoading(true);

101

setError(null);

102

103

try {

104

const response = await api.searchUsers(inputValue);

105

return response.data;

106

} catch (err) {

107

setError('Failed to load options');

108

return [];

109

} finally {

110

setIsLoading(false);

111

}

112

};

113

114

return (

115

<div>

116

<AsyncSelect

117

loadOptions={loadOptions}

118

isLoading={isLoading}

119

placeholder={error ? error : "Search users..."}

120

/>

121

</div>

122

);

123

};

124

```

125

126

### Default Options Configuration

127

128

Control initial option loading behavior and caching strategies.

129

130

```typescript { .api }

131

/**

132

* defaultOptions configuration for AsyncSelect

133

*/

134

type DefaultOptions<Option, Group extends GroupBase<Option>> =

135

| OptionsOrGroups<Option, Group> // Specific default options

136

| boolean; // true = load immediately, false = no defaults

137

138

/**

139

* cacheOptions configuration for result caching

140

*/

141

type CacheOptions = any; // Enable/disable caching of loadOptions results

142

```

143

144

**Usage Examples:**

145

146

```typescript

147

// Preload specific default options

148

const PreloadedAsync = () => (

149

<AsyncSelect

150

defaultOptions={[

151

{ value: 'popular1', label: 'Popular Option 1' },

152

{ value: 'popular2', label: 'Popular Option 2' },

153

]}

154

loadOptions={loadOptions}

155

/>

156

);

157

158

// Auto-load on mount

159

const AutoLoadAsync = () => (

160

<AsyncSelect

161

defaultOptions={true} // Calls loadOptions('') on mount

162

loadOptions={loadOptions}

163

cacheOptions={true} // Cache results

164

/>

165

);

166

167

// No defaults, load only on input

168

const OnDemandAsync = () => (

169

<AsyncSelect

170

defaultOptions={false} // No initial options

171

loadOptions={loadOptions}

172

placeholder="Start typing to search..."

173

/>

174

);

175

```

176

177

### Load Options Function Patterns

178

179

Different patterns for implementing the loadOptions function.

180

181

```typescript { .api }

182

/**

183

* Load options function - Promise pattern

184

* @param inputValue - Current input value for filtering

185

* @returns Promise resolving to options array

186

*/

187

type LoadOptionsPromise<Option, Group extends GroupBase<Option>> = (

188

inputValue: string

189

) => Promise<OptionsOrGroups<Option, Group>>;

190

191

/**

192

* Load options function - Callback pattern

193

* @param inputValue - Current input value for filtering

194

* @param callback - Function to call with loaded options

195

*/

196

type LoadOptionsCallback<Option, Group extends GroupBase<Option>> = (

197

inputValue: string,

198

callback: (options: OptionsOrGroups<Option, Group>) => void

199

) => void;

200

```

201

202

**Usage Examples:**

203

204

```typescript

205

// REST API integration

206

const restApiLoader = async (inputValue: string) => {

207

const params = new URLSearchParams({

208

search: inputValue,

209

limit: '20',

210

});

211

212

const response = await fetch(`/api/options?${params}`);

213

if (!response.ok) throw new Error('Failed to fetch');

214

215

const data = await response.json();

216

return data.items.map((item: any) => ({

217

value: item.id,

218

label: item.displayName,

219

...item,

220

}));

221

};

222

223

// GraphQL integration

224

const graphqlLoader = async (inputValue: string) => {

225

const query = `

226

query SearchOptions($search: String!) {

227

searchOptions(search: $search) {

228

id

229

name

230

description

231

}

232

}

233

`;

234

235

const response = await graphqlClient.query({

236

query,

237

variables: { search: inputValue },

238

});

239

240

return response.data.searchOptions.map((item: any) => ({

241

value: item.id,

242

label: item.name,

243

meta: item.description,

244

}));

245

};

246

247

// Debounced loading

248

const debouncedLoader = useMemo(

249

() => debounce(async (inputValue: string) => {

250

return await api.search(inputValue);

251

}, 300),

252

[]

253

);

254

255

// Grouped options loading

256

const groupedLoader = async (inputValue: string) => {

257

const [users, teams] = await Promise.all([

258

api.searchUsers(inputValue),

259

api.searchTeams(inputValue),

260

]);

261

262

return [

263

{

264

label: 'Users',

265

options: users.map(user => ({ value: user.id, label: user.name })),

266

},

267

{

268

label: 'Teams',

269

options: teams.map(team => ({ value: team.id, label: team.name })),

270

},

271

];

272

};

273

```

274

275

### useAsync Hook

276

277

Hook for implementing custom async behavior with select components.

278

279

```typescript { .api }

280

/**

281

* Hook for managing async select state and behavior

282

* @param props - Async hook configuration

283

* @returns Object with async state and handlers

284

*/

285

function useAsync<Option, Group extends GroupBase<Option>>(

286

props: UseAsyncProps<Option, Group>

287

): UseAsyncResult<Option, Group>;

288

289

interface UseAsyncProps<Option, Group extends GroupBase<Option>> {

290

defaultOptions?: OptionsOrGroups<Option, Group> | boolean;

291

cacheOptions?: any;

292

loadOptions?: LoadOptionsFunction<Option, Group>;

293

}

294

295

interface UseAsyncResult<Option, Group extends GroupBase<Option>> {

296

/** Current loaded options */

297

options: OptionsOrGroups<Option, Group>;

298

/** Whether async loading is in progress */

299

isLoading: boolean;

300

/** Current input value being searched */

301

inputValue: string;

302

/** Handler for input changes that triggers loading */

303

onInputChange: (inputValue: string, actionMeta: InputActionMeta) => void;

304

/** Handler for menu open events */

305

onMenuOpen: () => void;

306

}

307

```

308

309

**Usage Examples:**

310

311

```typescript

312

import { BaseSelect, useAsync } from "react-select";

313

314

// Custom async select with additional logic

315

const CustomAsyncSelect = (props) => {

316

const asyncProps = useAsync({

317

defaultOptions: true,

318

cacheOptions: true,

319

loadOptions: props.loadOptions,

320

});

321

322

return (

323

<BaseSelect

324

{...props}

325

{...asyncProps}

326

filterOption={null} // Disable client-side filtering

327

/>

328

);

329

};

330

331

// Async with custom loading states

332

const EnhancedAsyncSelect = () => {

333

const [error, setError] = useState(null);

334

335

const loadOptions = async (inputValue: string) => {

336

try {

337

setError(null);

338

return await api.search(inputValue);

339

} catch (err) {

340

setError(err.message);

341

return [];

342

}

343

};

344

345

const asyncProps = useAsync({

346

loadOptions,

347

cacheOptions: true,

348

});

349

350

return (

351

<div>

352

{error && <div className="error">{error}</div>}

353

<BaseSelect

354

{...asyncProps}

355

loadingMessage={() => "Searching..."}

356

noOptionsMessage={({ inputValue }) =>

357

error ? "Error loading options" : `No results for "${inputValue}"`

358

}

359

/>

360

</div>

361

);

362

};

363

```

364

365

### AsyncCreatableSelect Integration

366

367

Combining async loading with option creation capabilities.

368

369

```typescript { .api }

370

/**

371

* Select combining async loading and option creation

372

* @param props - Combined async and creatable props

373

* @returns JSX.Element with both capabilities

374

*/

375

function AsyncCreatableSelect<

376

Option = unknown,

377

IsMulti extends boolean = false,

378

Group extends GroupBase<Option> = GroupBase<Option>

379

>(

380

props: Props<Option, IsMulti, Group> & AsyncProps<Option> & CreatableProps<Option>

381

): JSX.Element;

382

383

interface AsyncCreatableProps<Option> extends AsyncProps<Option>, CreatableProps<Option> {

384

/** Allow creating options while async loading is in progress */

385

allowCreateWhileLoading?: boolean;

386

}

387

```

388

389

**Usage Examples:**

390

391

```typescript

392

import AsyncCreatableSelect from "react-select/async-creatable";

393

394

// Tags with async loading and creation

395

const TagsAsyncCreatable = () => {

396

const loadOptions = async (inputValue: string) => {

397

const response = await api.searchTags(inputValue);

398

return response.map(tag => ({ value: tag.id, label: tag.name }));

399

};

400

401

const handleCreate = async (inputValue: string) => {

402

const newTag = await api.createTag({ name: inputValue });

403

return { value: newTag.id, label: newTag.name };

404

};

405

406

return (

407

<AsyncCreatableSelect

408

isMulti

409

cacheOptions

410

defaultOptions={false}

411

loadOptions={loadOptions}

412

onCreateOption={handleCreate}

413

allowCreateWhileLoading={true}

414

formatCreateLabel={(inputValue) => `Create tag "${inputValue}"`}

415

noOptionsMessage={({ inputValue }) =>

416

inputValue ? `No existing tags found. Type to create "${inputValue}"` : "Start typing to search tags"

417

}

418

/>

419

);

420

};

421

```

422

423

## Performance Optimization

424

425

### Caching Strategies

426

427

```typescript

428

// Memory-efficient caching with size limits

429

const createCachedLoader = (maxCacheSize = 100) => {

430

const cache = new Map();

431

432

return async (inputValue: string) => {

433

if (cache.has(inputValue)) {

434

return cache.get(inputValue);

435

}

436

437

const result = await api.search(inputValue);

438

439

// Implement LRU cache

440

if (cache.size >= maxCacheSize) {

441

const firstKey = cache.keys().next().value;

442

cache.delete(firstKey);

443

}

444

445

cache.set(inputValue, result);

446

return result;

447

};

448

};

449

450

// Time-based cache expiration

451

const createTimeCachedLoader = (ttlMs = 300000) => { // 5 minutes

452

const cache = new Map();

453

454

return async (inputValue: string) => {

455

const cached = cache.get(inputValue);

456

if (cached && Date.now() - cached.timestamp < ttlMs) {

457

return cached.data;

458

}

459

460

const result = await api.search(inputValue);

461

cache.set(inputValue, {

462

data: result,

463

timestamp: Date.now(),

464

});

465

466

return result;

467

};

468

};

469

```

470

471

### Debouncing and Throttling

472

473

```typescript

474

// Debounced loading to reduce API calls

475

const useDebouncedAsync = (loadOptions: Function, delay = 300) => {

476

const debouncedLoader = useMemo(

477

() => debounce(loadOptions, delay),

478

[loadOptions, delay]

479

);

480

481

return debouncedLoader;

482

};

483

484

// Usage

485

const DebouncedAsyncSelect = () => {

486

const baseLoader = async (inputValue: string) => {

487

return await api.search(inputValue);

488

};

489

490

const debouncedLoader = useDebouncedAsync(baseLoader, 500);

491

492

return (

493

<AsyncSelect

494

loadOptions={debouncedLoader}

495

cacheOptions

496

/>

497

);

498

};

499

```