0
# React Integration
1
2
React hooks and components for building reactive user interfaces with Legend State. The React integration provides optimized rendering with minimal re-renders through fine-grained dependency tracking.
3
4
## Capabilities
5
6
### React Hooks
7
8
#### Observable Hook
9
10
Creates and manages observables within React components.
11
12
```typescript { .api }
13
/**
14
* Creates an observable state within a React component
15
* @param initialValue - Initial value, function returning value, or async function
16
* @returns Observable instance tied to component lifecycle
17
*/
18
function useObservable<T>(initialValue?: T | (() => T) | (() => Promise<T>)): Observable<T>;
19
```
20
21
#### Observe Effect Hook
22
23
Observes observable changes and runs effects when dependencies update.
24
25
```typescript { .api }
26
/**
27
* Observes changes and runs effect function when dependencies change
28
* @param selector - Function that accesses observables to track
29
* @param effect - Effect function to run on changes
30
*/
31
function useObserveEffect<T>(
32
selector: () => T,
33
effect: (value: T) => void
34
): void;
35
36
/**
37
* Alternative observe hook for direct observable observation
38
* @param selector - Function accessing observables
39
* @param reaction - Reaction function for changes
40
*/
41
function useObserve<T>(
42
selector: () => T,
43
reaction: (value: T) => void
44
): void;
45
```
46
47
#### Selector Hook
48
49
Subscribes to observable values with automatic re-rendering.
50
51
```typescript { .api }
52
/**
53
* Subscribes to observable selector with automatic re-rendering
54
* @param selector - Function selecting observable values
55
* @returns Current value from selector
56
*/
57
function useSelector<T>(selector: () => T): T;
58
```
59
60
#### Computed Hook
61
62
Creates computed values within React components.
63
64
```typescript { .api }
65
/**
66
* Creates a computed value within React component
67
* @param compute - Function to compute the value
68
* @returns Computed value that updates automatically
69
*/
70
function useComputed<T>(compute: () => T): T;
71
```
72
73
#### When Hook
74
75
Provides Promise-based waiting within React components.
76
77
```typescript { .api }
78
/**
79
* Returns Promise that resolves when predicate becomes truthy
80
* @param predicate - Function to evaluate
81
* @param effect - Optional effect to run when resolved
82
* @returns Promise resolving to truthy value
83
*/
84
function useWhen<T>(
85
predicate: () => T,
86
effect?: (value: T) => void
87
): Promise<T>;
88
```
89
90
#### Observable Reducer Hook
91
92
Provides Redux-like pattern with observables.
93
94
```typescript { .api }
95
/**
96
* Creates observable state with reducer pattern
97
* @param reducer - Reducer function for state updates
98
* @param initialState - Initial state value
99
* @returns Tuple of observable state and dispatch function
100
*/
101
function useObservableReducer<T, A>(
102
reducer: (state: T, action: A) => T,
103
initialState: T
104
): [Observable<T>, (action: A) => void];
105
```
106
107
### Lifecycle Hooks
108
109
Utility hooks for component lifecycle management.
110
111
```typescript { .api }
112
/**
113
* Runs effect only once on component mount
114
* @param effect - Effect function to run on mount
115
*/
116
function useEffectOnce(effect: () => void | (() => void)): void;
117
118
/**
119
* Runs effect on component mount
120
* @param effect - Effect function to run on mount
121
*/
122
function useMount(effect: () => void): void;
123
124
/**
125
* Runs effect on component unmount
126
* @param effect - Effect function to run on unmount
127
*/
128
function useUnmount(effect: () => void): void;
129
130
/**
131
* Returns whether component is currently mounted
132
* @returns Boolean indicating mount status
133
*/
134
function useIsMounted(): boolean;
135
```
136
137
### React Components
138
139
#### Reactive Components
140
141
Higher-order components that automatically re-render when observables change.
142
143
```typescript { .api }
144
/**
145
* Makes any HTML element reactive to observable changes
146
* Generic component that works with all HTML elements
147
*/
148
declare const Reactive: {
149
[K in keyof JSX.IntrinsicElements]: React.ComponentType<
150
JSX.IntrinsicElements[K] & {
151
$key?: Observable<string | number>;
152
}
153
>;
154
};
155
156
/**
157
* HOC to make any component reactive to observable changes
158
* @param Component - Component to make reactive
159
* @returns Reactive version of the component
160
*/
161
function reactive<T extends React.ComponentType<any>>(Component: T): T;
162
163
/**
164
* Configuration for reactive system
165
* @param config - Configuration options
166
*/
167
function configureReactive(config: ReactiveConfig): void;
168
169
interface ReactiveConfig {
170
/** Whether to track changes automatically */
171
autoTrack?: boolean;
172
/** Whether to use direct rendering optimizations */
173
directRender?: boolean;
174
}
175
```
176
177
#### Control Flow Components
178
179
Components for conditional rendering and iteration.
180
181
```typescript { .api }
182
/**
183
* Conditional rendering component
184
* @param props.if - Selector condition to evaluate
185
* @param props.ifReady - Alternative selector for ready state
186
* @param props.else - Optional fallback content
187
* @param props.children - Content to render when condition is true
188
*/
189
function Show<T>(props: {
190
if?: Selector<T>;
191
ifReady?: Selector<T>;
192
else?: React.ReactNode | (() => React.ReactNode);
193
wrap?: React.ComponentType;
194
children: React.ReactNode | ((value?: T) => React.ReactNode);
195
}): JSX.Element;
196
197
/**
198
* Switch/case rendering component
199
* @param props.value - Observable value to switch on
200
* @param props.children - Switch cases as children
201
*/
202
function Switch<T>(props: {
203
value: Observable<T> | (() => T);
204
children: React.ReactNode;
205
}): JSX.Element;
206
207
/**
208
* Array iteration component with optimized rendering
209
* @param props.each - Observable array, object, or Map to iterate
210
* @param props.item - Component for rendering each item
211
* @param props.children - Alternative render function for each item
212
* @param props.optimized - Whether to use optimization
213
*/
214
function For<T, TProps = {}>(props: {
215
each?: ObservableReadable<T[] | Record<any, T> | Map<any, T>>;
216
item?: React.ComponentType<{ item$: Observable<T>; id?: string } & TProps>;
217
itemProps?: TProps;
218
children?: (item: Observable<T>, id: string | undefined) => React.ReactElement;
219
optimized?: boolean;
220
sortValues?: (A: T, B: T, AKey: string, BKey: string) => number;
221
}): JSX.Element;
222
223
/**
224
* Memoized component wrapper
225
* @param props.children - Function returning content to memoize
226
*/
227
function Memo<T>(props: {
228
children: () => React.ReactNode;
229
}): JSX.Element;
230
231
/**
232
* Computed value component
233
* @param props.children - Function returning computed content
234
*/
235
function Computed<T>(props: {
236
children: () => React.ReactNode;
237
}): JSX.Element;
238
```
239
240
#### Provider Components
241
242
Context providers for pause/resume functionality.
243
244
```typescript { .api }
245
/**
246
* Provides pause context to child components
247
* @param paused - Whether to pause reactive updates
248
* @returns Provider component
249
*/
250
function usePauseProvider(paused: boolean): React.ComponentType<{
251
children: React.ReactNode;
252
}>;
253
```
254
255
### Extended React Hooks
256
257
Additional hooks for common UI patterns and integrations.
258
259
```typescript { .api }
260
/**
261
* Hook for persisted observable state
262
* @param key - Storage key for persistence
263
* @param initialValue - Initial value if not persisted
264
* @returns Persisted observable
265
*/
266
function usePersistedObservable<T>(
267
key: string,
268
initialValue?: T
269
): Observable<T>;
270
271
/**
272
* Hook for observable queries with loading/error states
273
* @param queryFn - Function returning Promise of data
274
* @param options - Query configuration options
275
* @returns Observable query state
276
*/
277
function useObservableQuery<T>(
278
queryFn: () => Promise<T>,
279
options?: QueryOptions
280
): ObservableQuery<T>;
281
282
interface QueryOptions {
283
enabled?: boolean;
284
refetchInterval?: number;
285
staleTime?: number;
286
}
287
288
interface ObservableQuery<T> {
289
data: Observable<T | undefined>;
290
error: Observable<Error | undefined>;
291
isLoading: Observable<boolean>;
292
isError: Observable<boolean>;
293
refetch: () => Promise<void>;
294
}
295
296
/**
297
* Hook for fetch operations with observable state
298
* @param url - URL to fetch
299
* @param options - Fetch options
300
* @returns Observable fetch state
301
*/
302
function useFetch<T>(
303
url: string,
304
options?: RequestInit
305
): ObservableFetch<T>;
306
307
interface ObservableFetch<T> {
308
data: Observable<T | undefined>;
309
error: Observable<Error | undefined>;
310
loading: Observable<boolean>;
311
}
312
313
/**
314
* Hook for hover state tracking
315
* @returns Hover state and ref
316
*/
317
function useHover(): [Observable<boolean>, React.RefObject<HTMLElement>];
318
319
/**
320
* Hook for element measurements
321
* @returns Measurements and ref
322
*/
323
function useMeasure(): [
324
Observable<{ width: number; height: number }>,
325
React.RefObject<HTMLElement>
326
];
327
328
/**
329
* Creates observable from any hook
330
* @param hookFn - Hook function to make observable
331
* @returns Hook that returns observable
332
*/
333
function createObservableHook<T>(hookFn: () => T): () => Observable<T>;
334
```
335
336
**Usage Examples:**
337
338
```typescript
339
import React from "react";
340
import { observable } from "@legendapp/state";
341
import {
342
useObservable,
343
useSelector,
344
Reactive,
345
Show,
346
For
347
} from "@legendapp/state/react";
348
349
// Component with local observable state
350
function TodoApp() {
351
const todos$ = useObservable([
352
{ id: 1, text: "Learn Legend State", done: false },
353
{ id: 2, text: "Build awesome app", done: false }
354
]);
355
356
const newTodo$ = useObservable("");
357
358
const addTodo = () => {
359
const text = newTodo$.get().trim();
360
if (text) {
361
todos$.push({
362
id: Date.now(),
363
text,
364
done: false
365
});
366
newTodo$.set("");
367
}
368
};
369
370
return (
371
<div>
372
<input
373
value={useSelector(() => newTodo$.get())}
374
onChange={e => newTodo$.set(e.target.value)}
375
onKeyPress={e => e.key === 'Enter' && addTodo()}
376
/>
377
<button onClick={addTodo}>Add Todo</button>
378
379
<For each={todos$} item={(todo$, index$) => (
380
<Reactive.div key={todo$.id.get()}>
381
<input
382
type="checkbox"
383
checked={todo$.done.get()}
384
onChange={e => todo$.done.set(e.target.checked)}
385
/>
386
<span style={{
387
textDecoration: todo$.done.get() ? 'line-through' : 'none'
388
}}>
389
{todo$.text.get()}
390
</span>
391
<button onClick={() => todos$.splice(index$.get(), 1)}>
392
Delete
393
</button>
394
</Reactive.div>
395
)} />
396
397
<Show if={() => todos$.get().length === 0}>
398
<p>No todos yet!</p>
399
</Show>
400
</div>
401
);
402
}
403
404
// Global state example
405
const appState$ = observable({
406
user: null,
407
theme: "light",
408
notifications: []
409
});
410
411
function Header() {
412
const theme = useSelector(() => appState$.theme.get());
413
414
return (
415
<Reactive.header className={`header theme-${theme}`}>
416
<Show
417
if={() => appState$.user.get()}
418
else={<button>Login</button>}
419
>
420
{user => <span>Welcome, {user.name}!</span>}
421
</Show>
422
</Reactive.header>
423
);
424
}
425
```