0
# Immutability Helpers
1
2
Data update utilities for managing immutable state transformations safely and efficiently.
3
4
## Capabilities
5
6
### Update Function
7
8
Immutable update utility based on MongoDB's query language for safe state modifications.
9
10
```javascript { .api }
11
/**
12
* Immutably update nested data structures
13
* @param {any} object - Object to update
14
* @param {UpdateSpec} spec - Update specification
15
* @returns {any} New object with updates applied
16
*/
17
function update(object, spec);
18
19
/**
20
* Update specification object
21
*/
22
interface UpdateSpec {
23
/** Set a value */
24
$set?: any;
25
26
/** Merge an object (shallow merge) */
27
$merge?: object;
28
29
/** Push items to end of array */
30
$push?: Array<any>;
31
32
/** Add items to beginning of array */
33
$unshift?: Array<any>;
34
35
/** Splice array (remove/insert items) */
36
$splice?: Array<[number, number, ...any[]]>;
37
38
/** Apply function to transform value */
39
$apply?: (value: any) => any;
40
41
/** Toggle boolean value */
42
$toggle?: Array<string>;
43
44
/** Remove items from array by index */
45
$unset?: Array<string | number>;
46
47
/** Nested updates - any key can contain another UpdateSpec */
48
[key: string]: UpdateSpec | any;
49
}
50
```
51
52
**Usage Examples:**
53
54
```javascript
55
import update from 'preact-compat/lib/update';
56
57
// Initial state
58
const state = {
59
user: {
60
name: 'John',
61
profile: {
62
age: 30,
63
preferences: ['music', 'sports']
64
}
65
},
66
items: [1, 2, 3],
67
settings: {
68
theme: 'light',
69
notifications: true
70
}
71
};
72
73
// Update nested object
74
const newState1 = update(state, {
75
user: {
76
name: { $set: 'Jane' },
77
profile: {
78
age: { $set: 31 },
79
preferences: { $push: ['reading'] }
80
}
81
}
82
});
83
84
// Merge objects
85
const newState2 = update(state, {
86
settings: { $merge: { theme: 'dark', language: 'en' } }
87
});
88
89
// Array operations
90
const newState3 = update(state, {
91
items: { $splice: [[1, 1, 10, 11]] } // Remove 1 item at index 1, insert 10, 11
92
});
93
94
// Apply function transformation
95
const newState4 = update(state, {
96
user: {
97
profile: {
98
age: { $apply: age => age + 1 }
99
}
100
}
101
});
102
```
103
104
### Common Update Patterns
105
106
```javascript
107
import update from 'preact-compat/lib/update';
108
109
class TodoApp extends Component {
110
state = {
111
todos: [
112
{ id: 1, text: 'Learn React', completed: false },
113
{ id: 2, text: 'Build app', completed: false }
114
],
115
filter: 'all'
116
};
117
118
// Add new todo
119
addTodo = (text) => {
120
const newTodo = {
121
id: Date.now(),
122
text,
123
completed: false
124
};
125
126
this.setState(update(this.state, {
127
todos: { $push: [newTodo] }
128
}));
129
};
130
131
// Toggle todo completion
132
toggleTodo = (id) => {
133
const todoIndex = this.state.todos.findIndex(todo => todo.id === id);
134
135
this.setState(update(this.state, {
136
todos: {
137
[todoIndex]: {
138
completed: { $apply: completed => !completed }
139
}
140
}
141
}));
142
};
143
144
// Remove todo
145
removeTodo = (id) => {
146
const todoIndex = this.state.todos.findIndex(todo => todo.id === id);
147
148
this.setState(update(this.state, {
149
todos: { $splice: [[todoIndex, 1]] }
150
}));
151
};
152
153
// Update filter
154
setFilter = (filter) => {
155
this.setState(update(this.state, {
156
filter: { $set: filter }
157
}));
158
};
159
160
// Bulk operations
161
completeAll = () => {
162
this.setState(update(this.state, {
163
todos: { $apply: todos =>
164
todos.map(todo => ({ ...todo, completed: true }))
165
}
166
}));
167
};
168
169
render() {
170
const { todos, filter } = this.state;
171
172
return (
173
<div>
174
<input
175
onKeyPress={(e) => {
176
if (e.key === 'Enter') {
177
this.addTodo(e.target.value);
178
e.target.value = '';
179
}
180
}}
181
placeholder="Add todo..."
182
/>
183
<button onClick={this.completeAll}>Complete All</button>
184
185
{todos.map(todo => (
186
<div key={todo.id}>
187
<input
188
type="checkbox"
189
checked={todo.completed}
190
onChange={() => this.toggleTodo(todo.id)}
191
/>
192
<span style={{
193
textDecoration: todo.completed ? 'line-through' : 'none'
194
}}>
195
{todo.text}
196
</span>
197
<button onClick={() => this.removeTodo(todo.id)}>Remove</button>
198
</div>
199
))}
200
</div>
201
);
202
}
203
}
204
```
205
206
### Advanced Update Patterns
207
208
```javascript
209
import update from 'preact-compat/lib/update';
210
211
// Complex nested updates
212
const complexState = {
213
users: {
214
byId: {
215
1: { name: 'John', posts: [10, 20] },
216
2: { name: 'Jane', posts: [30] }
217
},
218
allIds: [1, 2]
219
},
220
posts: {
221
byId: {
222
10: { title: 'Post 1', likes: 5 },
223
20: { title: 'Post 2', likes: 3 },
224
30: { title: 'Post 3', likes: 8 }
225
},
226
allIds: [10, 20, 30]
227
}
228
};
229
230
// Add new user with post
231
function addUserWithPost(state, user, post) {
232
return update(state, {
233
users: {
234
byId: { $merge: { [user.id]: user } },
235
allIds: { $push: [user.id] }
236
},
237
posts: {
238
byId: { $merge: { [post.id]: post } },
239
allIds: { $push: [post.id] }
240
}
241
});
242
}
243
244
// Update multiple related entities
245
function likePost(state, postId, userId) {
246
return update(state, {
247
posts: {
248
byId: {
249
[postId]: {
250
likes: { $apply: likes => likes + 1 }
251
}
252
}
253
},
254
users: {
255
byId: {
256
[userId]: {
257
likedPosts: { $push: [postId] }
258
}
259
}
260
}
261
});
262
}
263
264
// Conditional updates
265
function updateUserIfExists(state, userId, updates) {
266
if (state.users.byId[userId]) {
267
return update(state, {
268
users: {
269
byId: {
270
[userId]: { $merge: updates }
271
}
272
}
273
});
274
}
275
return state;
276
}
277
```
278
279
## Import Patterns
280
281
```javascript
282
// Main import
283
import update from 'preact-compat/lib/update';
284
285
// CommonJS
286
const update = require('preact-compat/lib/update');
287
```
288
289
## Types
290
291
```javascript { .api }
292
interface UpdateSpec {
293
$set?: any;
294
$merge?: object;
295
$push?: Array<any>;
296
$unshift?: Array<any>;
297
$splice?: Array<SpliceOperation>;
298
$apply?: (value: any) => any;
299
$toggle?: Array<string>;
300
$unset?: Array<string | number>;
301
[key: string]: UpdateSpec | any;
302
}
303
304
type SpliceOperation = [number, number, ...any[]];
305
306
type UpdateFunction = <T>(object: T, spec: UpdateSpec) => T;
307
```