0
# State Definition
1
2
Core functions for defining atoms and selectors that form the foundation of Recoil's state graph. Atoms represent units of state, while selectors represent derived state or computations.
3
4
## Capabilities
5
6
### Atom Definition
7
8
Creates a unit of state that components can read from and write to.
9
10
```typescript { .api }
11
/**
12
* Creates an atom, which represents a piece of writeable state
13
*/
14
function atom<T>(options: AtomOptions<T>): RecoilState<T>;
15
16
type AtomOptions<T> = {
17
/** Unique string identifying this atom */
18
key: string;
19
/** Default value for the atom */
20
default?: T | RecoilValue<T> | Promise<T> | Loadable<T> | WrappedValue<T>;
21
/** Array of effects to run when atom is first used */
22
effects?: ReadonlyArray<AtomEffect<T>>;
23
/** Allow direct mutation of atom values (use with caution) */
24
dangerouslyAllowMutability?: boolean;
25
};
26
```
27
28
**Usage Examples:**
29
30
```typescript
31
import { atom } from 'recoil';
32
33
// Simple atom with primitive default
34
const countState = atom({
35
key: 'countState',
36
default: 0,
37
});
38
39
// Atom with object default
40
const userState = atom({
41
key: 'userState',
42
default: {
43
id: null,
44
name: '',
45
email: '',
46
},
47
});
48
49
// Atom with async default
50
const userProfileState = atom({
51
key: 'userProfileState',
52
default: fetch('/api/user').then(res => res.json()),
53
});
54
55
// Atom with effects
56
const persistedState = atom({
57
key: 'persistedState',
58
default: '',
59
effects: [
60
({setSelf, onSet}) => {
61
// Initialize from localStorage
62
const saved = localStorage.getItem('persistedState');
63
if (saved != null) {
64
setSelf(JSON.parse(saved));
65
}
66
67
// Save to localStorage on changes
68
onSet((newValue) => {
69
localStorage.setItem('persistedState', JSON.stringify(newValue));
70
});
71
},
72
],
73
});
74
```
75
76
### Atom Value Wrapping
77
78
Utility for preventing automatic unwrapping of values in atoms and selectors.
79
80
```typescript { .api }
81
namespace atom {
82
/**
83
* Wraps a value to prevent unwrapping by Recoil
84
*/
85
function value<T>(value: T): WrappedValue<T>;
86
}
87
88
interface WrappedValue<T> {
89
readonly [WrappedValue_OPAQUE]: true;
90
}
91
```
92
93
**Usage Examples:**
94
95
```typescript
96
import { atom } from 'recoil';
97
98
// Prevent unwrapping of promises
99
const promiseState = atom({
100
key: 'promiseState',
101
default: atom.value(Promise.resolve('value')), // Promise won't be unwrapped
102
});
103
104
// Prevent unwrapping of Recoil values
105
const wrappedAtomState = atom({
106
key: 'wrappedAtomState',
107
default: atom.value(someOtherAtom), // Atom won't be read
108
});
109
```
110
111
### Selector Definition
112
113
Creates derived state that can depend on atoms or other selectors.
114
115
```typescript { .api }
116
/**
117
* Creates a selector which represents derived state
118
*/
119
function selector<T>(options: ReadWriteSelectorOptions<T>): RecoilState<T>;
120
function selector<T>(options: ReadOnlySelectorOptions<T>): RecoilValueReadOnly<T>;
121
122
interface ReadOnlySelectorOptions<T> {
123
/** Unique string identifying this selector */
124
key: string;
125
/** Function that computes the selector's value */
126
get: (opts: {
127
get: GetRecoilValue;
128
getCallback: GetCallback;
129
}) => T | RecoilValue<T> | Promise<T> | Loadable<T> | WrappedValue<T>;
130
/** Cache policy for this selector */
131
cachePolicy_UNSTABLE?: CachePolicyWithoutEquality;
132
/** Allow direct mutation of selector values (use with caution) */
133
dangerouslyAllowMutability?: boolean;
134
}
135
136
interface ReadWriteSelectorOptions<T> extends ReadOnlySelectorOptions<T> {
137
/** Function that handles setting the selector's value */
138
set: (opts: {
139
set: SetRecoilState;
140
get: GetRecoilValue;
141
reset: ResetRecoilState;
142
}, newValue: T | DefaultValue) => void;
143
}
144
```
145
146
**Usage Examples:**
147
148
```typescript
149
import { atom, selector, DefaultValue } from 'recoil';
150
151
const textState = atom({
152
key: 'textState',
153
default: '',
154
});
155
156
// Read-only selector
157
const charCountState = selector({
158
key: 'charCountState',
159
get: ({get}) => {
160
const text = get(textState);
161
return text.length;
162
},
163
});
164
165
// Async selector
166
const userNameState = selector({
167
key: 'userNameState',
168
get: async ({get}) => {
169
const userID = get(currentUserIDState);
170
const response = await fetch(`/api/users/${userID}`);
171
return response.json();
172
},
173
});
174
175
// Read-write selector
176
const tempFahrenheit = selector({
177
key: 'tempFahrenheit',
178
get: ({get}) => {
179
const tempCelsius = get(tempCelsiusState);
180
return (tempCelsius * 9) / 5 + 32;
181
},
182
set: ({set}, newValue) => {
183
const tempCelsius = ((newValue as number) - 32) * 5 / 9;
184
set(tempCelsiusState, tempCelsius);
185
},
186
});
187
188
// Selector with error handling
189
const safeDataState = selector({
190
key: 'safeDataState',
191
get: async ({get}) => {
192
try {
193
const data = await get(asyncDataState);
194
return data;
195
} catch (error) {
196
return { error: error.message };
197
}
198
},
199
});
200
```
201
202
### Selector Value Wrapping
203
204
Utility for preventing automatic unwrapping of values in selectors.
205
206
```typescript { .api }
207
namespace selector {
208
/**
209
* Wraps a value to prevent unwrapping by Recoil
210
*/
211
function value<T>(value: T): WrappedValue<T>;
212
}
213
```
214
215
### Convenience Selectors
216
217
Pre-built selectors for common use cases.
218
219
```typescript { .api }
220
/**
221
* Returns a selector that always has a constant value
222
*/
223
function constSelector<T extends SerializableParam>(constant: T): RecoilValueReadOnly<T>;
224
225
/**
226
* Returns a selector which is always in the provided error state
227
*/
228
function errorSelector(message: string): RecoilValueReadOnly<never>;
229
230
/**
231
* Casts a selector to be a read-only selector
232
*/
233
function readOnlySelector<T>(atom: RecoilValue<T>): RecoilValueReadOnly<T>;
234
```
235
236
**Usage Examples:**
237
238
```typescript
239
import { constSelector, errorSelector, readOnlySelector } from 'recoil';
240
241
// Constant selector
242
const appVersionState = constSelector('1.0.0');
243
244
// Error selector for testing
245
const errorState = errorSelector('This always errors');
246
247
// Read-only wrapper
248
const readOnlyUserState = readOnlySelector(userState);
249
```
250
251
### Atom Effects
252
253
System for adding side effects to atoms when they are first used.
254
255
```typescript { .api }
256
type AtomEffect<T> = (param: {
257
node: RecoilState<T>;
258
storeID: StoreID;
259
trigger: 'set' | 'get';
260
setSelf: (param: T | DefaultValue | Promise<T | DefaultValue> | WrappedValue<T> |
261
((param: T | DefaultValue) => T | DefaultValue | WrappedValue<T>)) => void;
262
resetSelf: () => void;
263
onSet: (param: (newValue: T, oldValue: T | DefaultValue, isReset: boolean) => void) => void;
264
getPromise: <S>(recoilValue: RecoilValue<S>) => Promise<S>;
265
getLoadable: <S>(recoilValue: RecoilValue<S>) => Loadable<S>;
266
getInfo_UNSTABLE: <S>(recoilValue: RecoilValue<S>) => RecoilStateInfo<S>;
267
}) => void | (() => void);
268
```
269
270
**Usage Examples:**
271
272
```typescript
273
import { atom } from 'recoil';
274
275
// Persistence effect
276
const localStorageEffect = (key: string) => ({setSelf, onSet}) => {
277
const savedValue = localStorage.getItem(key);
278
if (savedValue != null) {
279
setSelf(JSON.parse(savedValue));
280
}
281
282
onSet((newValue, _, isReset) => {
283
isReset
284
? localStorage.removeItem(key)
285
: localStorage.setItem(key, JSON.stringify(newValue));
286
});
287
};
288
289
// Logging effect
290
const loggingEffect = ({onSet, node}) => {
291
onSet((newValue, oldValue) => {
292
console.log(`${node.key} changed from`, oldValue, 'to', newValue);
293
});
294
};
295
296
// Atom with effects
297
const trackedState = atom({
298
key: 'trackedState',
299
default: '',
300
effects: [
301
localStorageEffect('tracked-state'),
302
loggingEffect,
303
],
304
});
305
```
306
307
## Cache Policies
308
309
Configuration for selector result caching and eviction.
310
311
```typescript { .api }
312
type EvictionPolicy = 'lru' | 'keep-all' | 'most-recent';
313
314
type CachePolicyWithoutEquality =
315
| {eviction: 'lru', maxSize: number}
316
| {eviction: 'keep-all'}
317
| {eviction: 'most-recent'};
318
```