cogsbox-state 0.5.7 → 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +474 -280
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,386 +1,580 @@
1
- # Cobgsbox State
1
+ # Cogsbox State: A Practical Guide
2
2
 
3
- > ⚠️ **Warning**: This package is currently a work in progress and not ready for production use. The API is unstable and subject to breaking changes. Please do not use in production environments.
3
+ ## Getting Started
4
4
 
5
- ---
5
+ Cogsbox State is a React state management library that provides a fluent interface for managing complex state.
6
6
 
7
- Cobgsbox State is a state management library that provides a fluent interface for managing complex state in React applications.
7
+ ### Basic Setup
8
8
 
9
- ## Installation
9
+ ```typescript
10
+ // 1. Define your initial state
11
+ const InitialState = {
12
+ users: [],
13
+ settings: {
14
+ darkMode: false,
15
+ notifications: true
16
+ },
17
+ cart: {
18
+ items: [],
19
+ total: 0
20
+ }
21
+ };
10
22
 
11
- ```bash
12
- npm install cogsbox-state
13
- ```
23
+ // 2. Create the state hook
24
+ export const { useCogsState } = createCogsState(InitialState);
14
25
 
15
- ## Features
26
+ // 3. Use in your component
27
+ function MyComponent() {
28
+ const [state, updater] = useCogsState("cart");
16
29
 
17
- - 🎯 **Simplified Deep Updates**
30
+ // Access values
31
+ const cartItems = updater.items.get();
32
+ const total = updater.total.get();
18
33
 
19
- ```typescript
20
- // Instead of setState(prev => ({ ...prev, deep: { ...prev.deep, value: newValue }}))
21
- updater.deep.value.update(newValue);
22
- ```
34
+ // Update values
35
+ const addItem = (item) => {
36
+ updater.items.insert(item);
37
+ updater.total.update(total + item.price);
38
+ };
23
39
 
24
- - 🔄 **Fluent Array Operations**
40
+ return (
41
+ // Your component JSX
42
+ );
43
+ }
44
+ ```
25
45
 
26
- ```typescript
27
- // Find, filter, and update in one chain
28
- updater.items.findWith("id", "123").status.update("complete");
29
- ```
46
+ ## Core Concepts
30
47
 
31
- - 📝 **Smart Form Handling**
48
+ ### Accessing State
32
49
 
33
- - Automatic debouncing
34
- - Validation integration
35
- - Form state synchronization
50
+ ```typescript
51
+ // Get the entire state object
52
+ const entireCart = updater.get();
36
53
 
37
- - 🔍 **Powerful Array Methods**
54
+ // Access a specific property
55
+ const cartItems = updater.items.get();
38
56
 
39
- - Filter with state access
40
- - Flatten nested arrays
41
- - Unique insertions
42
- - Map with updaters
57
+ // Access nested properties
58
+ const firstItemPrice = updater.items[0].price.get();
59
+ ```
43
60
 
44
- - 🔄 **State Sync Features**
61
+ ### Updating State
45
62
 
46
- - Server synchronization
47
- - Local storage persistence
48
- - Optimistic updates
63
+ ```typescript
64
+ // Direct update
65
+ updater.settings.darkMode.update(true);
49
66
 
50
- - 🛠 **Developer Experience**
67
+ // Functional update (based on previous value)
68
+ updater.cart.total.update((prev) => prev + 10);
51
69
 
52
- - Full TypeScript support
53
- - Path autocompletion
54
- - Runtime type checking
70
+ // Deep update
71
+ updater.users.findWith("id", "123").name.update("New Name");
72
+ ```
55
73
 
56
- - **Performance**
57
- - Granular updates
58
- - Automatic optimization
59
- - Path-based subscriptions
74
+ ## Working with Arrays
60
75
 
61
- ## Initialization
76
+ ### Basic Array Operations
62
77
 
63
78
  ```typescript
64
- // Define your state shape
65
- interface Product {
66
- id: string;
67
- name: string;
68
- price: number;
69
- categories: string[];
70
- variants: {
71
- id: string;
72
- color: string;
73
- size: string;
74
- stock: number;
75
- }[];
76
- }
79
+ // Add an item
80
+ updater.cart.items.insert({ id: "prod1", name: "Product 1", price: 29.99 });
77
81
 
78
- interface CartItem {
79
- productId: string;
80
- variantId: string;
81
- quantity: number;
82
- }
83
-
84
- const InitialState = {
85
- catalog: {
86
- products: [] as Product[],
87
- categories: [] as string[],
88
- filters: {
89
- priceRange: { min: 0, max: 100 },
90
- selectedCategories: [] as string[],
91
- search: "",
92
- },
93
- sortBy: "price_asc" as "price_asc" | "price_desc" | "name",
94
- },
95
- cart: {
96
- items: [] as CartItem[],
97
- couponCode: "",
98
- shipping: {
99
- address: "",
100
- method: "standard" as "standard" | "express",
101
- cost: 0,
102
- },
103
- },
104
- ui: {
105
- sidebarOpen: false,
106
- activeProductId: null as string | null,
107
- notifications: [] as {
108
- id: string;
109
- message: string;
110
- type: "success" | "error";
111
- }[],
112
- },
113
- };
82
+ // Remove an item at index
83
+ updater.cart.items.cut(2);
114
84
 
115
- // Create state hook
116
- export const { useCogsState } = createCogsState(InitialState);
85
+ // Find and update an item
86
+ updater.cart.items.findWith("id", "prod1").quantity.update((prev) => prev + 1);
117
87
 
118
- // Use in component
119
- const [state, updater] = useCogsState("catalog");
88
+ // Update item at specific index
89
+ updater.cart.items.index(0).price.update(19.99);
120
90
  ```
121
91
 
122
- ## Updater API
92
+ ### Advanced Array Methods
123
93
 
124
- ### Basic Updates
94
+ ```typescript
95
+ // Map with access to updaters
96
+ updater.cart.items.stateMap((item, itemUpdater) => (
97
+ <CartItem
98
+ key={item.id}
99
+ item={item}
100
+ onQuantityChange={qty => itemUpdater.quantity.update(qty)}
101
+ />
102
+ ));
103
+
104
+ // Filter items while maintaining updater capabilities
105
+ const inStockItems = updater.products.stateFilter(product => product.stock > 0);
106
+
107
+ // Insert only if the item doesn't exist
108
+ updater.cart.items.uniqueInsert(
109
+ { id: "prod1", quantity: 1 },
110
+ ["id"] // fields to check for uniqueness
111
+ );
125
112
 
126
- #### `.update()`
113
+ // Flatten nested arrays by property
114
+ const allVariants = updater.products.stateFlattenOn("variants");
115
+ ```
127
116
 
128
- Updates state value directly.
117
+ ## Reactivity Control
129
118
 
130
- ```typescript
131
- // Direct update
132
- updater.catalog.filters.search.update("blue shoes");
119
+ Cogsbox offers different ways to control when components re-render:
133
120
 
134
- // Functional update
135
- updater.cart.items[0].quantity.update((prev) => prev + 1);
121
+ ### Component Reactivity (Default)
136
122
 
137
- // Deep update
138
- updater.catalog.products
139
- .findWith("id", "123")
140
- .variants[0].stock.update((prev) => prev - 1);
123
+ Re-renders when any accessed value changes.
124
+
125
+ ```typescript
126
+ // Default behavior - re-renders when cart.items or cart.total changes
127
+ const cart = useCogsState("cart");
128
+
129
+ return (
130
+ <div>
131
+ <div>Items: {cart.items.get().length}</div>
132
+ <div>Total: {cart.total.get()}</div>
133
+ </div>
134
+ );
141
135
  ```
142
136
 
143
- #### `.get()`
137
+ ### Dependency-Based Reactivity
144
138
 
145
- Gets current state value.
139
+ Re-renders only when specified dependencies change.
146
140
 
147
141
  ```typescript
148
- const searchTerm = updater.catalog.filters.search.get();
149
- const cartTotal = updater.cart.items
150
- .get()
151
- .reduce((sum, item) => sum + item.quantity, 0);
142
+ // Only re-renders when items array or status changes
143
+ const cart = useCogsState("cart", {
144
+ reactiveType: ["deps"],
145
+ reactiveDeps: (state) => [state.items, state.status],
146
+ });
152
147
  ```
153
148
 
154
- ### Array Operations
155
-
156
- #### `.insert()`
149
+ ### Full Reactivity
157
150
 
158
- Inserts an item into an array.
151
+ Re-renders on any state change, even for unused properties.
159
152
 
160
153
  ```typescript
161
- // Add product
162
- updater.catalog.products.insert({
163
- id: "123",
164
- name: "Running Shoes",
165
- price: 99.99,
166
- categories: ["shoes", "sports"],
167
- variants: [],
154
+ // Re-renders on any change to cart state
155
+ const cart = useCogsState("cart", {
156
+ reactiveType: ["all"],
168
157
  });
169
-
170
- // Add to cart
171
- updater.cart.items.insert((prev) => ({
172
- productId: "123",
173
- variantId: "v1",
174
- quantity: 1,
175
- }));
176
158
  ```
177
159
 
178
- #### `.uniqueInsert()`
160
+ ### Signal-Based Reactivity
179
161
 
180
- Inserts only if item doesn't exist (checks deep equality).
162
+ Updates only the DOM elements that depend on changed values.
181
163
 
182
164
  ```typescript
183
- // Add category if not exists
184
- updater.catalog.categories.uniqueInsert("sports");
185
-
186
- // Add notification with unique ID
187
- updater.ui.notifications.uniqueInsert(
188
- {
189
- id: generateId(),
190
- message: "Item added to cart",
191
- type: "success",
192
- },
193
- ["id"],
165
+ // Most efficient - updates just the specific DOM elements
166
+ return (
167
+ <div>
168
+ <div>Items: {cart.items.$derive(items => items.length)}</div>
169
+ <div>Total: {cart.total.$get()}</div>
170
+ </div>
194
171
  );
195
172
  ```
196
173
 
197
- #### `.cut()`
174
+ ## Form Integration
198
175
 
199
- Removes an item from an array.
176
+ Cogsbox State provides an intuitive form system to connect your state to form controls with built-in validation, error handling, and array support.
200
177
 
201
- ```typescript
202
- // Remove from cart
203
- updater.cart.items.cut(index);
178
+ ### Basic Form Element Usage
204
179
 
205
- // Remove notification
206
- updater.ui.notifications.findWith("id", "notif-123").cut();
207
- ```
208
-
209
- #### `.findWith()`
210
-
211
- Finds an item in array by property comparison.
180
+ The `formElement` method serves as the bridge between state and UI:
212
181
 
213
182
  ```typescript
214
- // Find and update product
215
- updater.catalog.products.findWith("id", 123).price.update(99.99);
216
-
217
- // Find and update cart item
218
- updater.cart.items
219
- .findWith("productId", 123)
220
- .quantity.update((prev) => prev + 1);
183
+ // Direct value/onChange pattern for complete control
184
+ user.firstName.formElement((params) => (
185
+ <div>
186
+ <label className="block text-sm font-medium">First Name</label>
187
+ <input
188
+ type="text"
189
+ className="mt-1 block w-full rounded-md border-2 p-2"
190
+ value={params.get()}
191
+ onChange={(e) => params.set(e.target.value)}
192
+ onBlur={params.inputProps.onBlur}
193
+ ref={params.inputProps.ref}
194
+ />
195
+ </div>
196
+ ));
197
+
198
+ // Using inputProps shorthand for simpler binding
199
+ user.lastName.formElement((params) => (
200
+ <div>
201
+ <label className="block text-sm font-medium">Last Name</label>
202
+ <input
203
+ type="text"
204
+ className="mt-1 block w-full rounded-md border-2 p-2"
205
+ {...params.inputProps}
206
+ />
207
+ </div>
208
+ ));
221
209
  ```
222
210
 
223
- #### `.index()`
211
+ ### Form Validation Options
224
212
 
225
- Gets item at specific index with updater methods.
213
+ Cogsbox provides several approaches to validation:
226
214
 
227
215
  ```typescript
228
- // Update first cart item
229
- updater.cart.items.index(0).quantity.update(2);
230
- ```
231
-
232
- #### `.stateEach()`
216
+ // Custom validation message
217
+ user.email.formElement(
218
+ (params) => (
219
+ <div>
220
+ <label>Email Address</label>
221
+ <input {...params.inputProps} type="email" />
222
+ </div>
223
+ ),
224
+ {
225
+ validation: {
226
+ message: "Please enter a valid email address"
227
+ }
228
+ }
229
+ );
233
230
 
234
- Maps over array with access to item updater.
231
+ // Hidden validation (show border but no message)
232
+ user.lastName.formElement(
233
+ (params) => (
234
+ <div>
235
+ <label>Last Name</label>
236
+ <input
237
+ {...params.inputProps}
238
+ className={`input ${params.validationErrors().length > 0 ? 'border-red-500' : ''}`}
239
+ />
240
+ </div>
241
+ ),
242
+ {
243
+ validation: {
244
+ hideMessage: true
245
+ }
246
+ }
247
+ );
235
248
 
236
- ```typescript
237
- // Apply discount to all products
238
- updater.catalog.products.stateEach((product, productUpdater) => {
239
- productUpdater.price.update((price) => price * 0.9);
240
- });
249
+ // Custom validation with onBlur
250
+ user.phone.formElement((params) => (
251
+ <div>
252
+ <label>Phone Number</label>
253
+ <input
254
+ {...params.inputProps}
255
+ onBlur={(e) => {
256
+ if (e.target.value.length == 0 || isNaN(Number(e.target.value))) {
257
+ params.addValidationError("Please enter a valid phone number");
258
+ }
259
+ }}
260
+ placeholder="(555) 123-4567"
261
+ />
262
+ </div>
263
+ ));
241
264
  ```
242
265
 
243
- #### `.stateFilter()`
266
+ ### Working with Form Arrays
244
267
 
245
- Creates filtered view of array with updater methods.
268
+ For managing collections like addresses:
246
269
 
247
270
  ```typescript
248
- // Get low stock variants
249
- const lowStock = updater.catalog.products.stateFilter((product) =>
250
- product.variants.some((v) => v.stock < 5),
251
- );
271
+ function AddressesManager() {
272
+ const [currentAddressIndex, setCurrentAddressIndex] = useState(0);
273
+ const user = useCogsState("user");
274
+
275
+ // Add new address
276
+ const addNewAddress = () => {
277
+ user.addresses.insert({
278
+ street: "",
279
+ city: "",
280
+ state: "",
281
+ zipCode: "",
282
+ country: "USA",
283
+ isDefault: false,
284
+ });
285
+ setCurrentAddressIndex(user.addresses.get().length - 1);
286
+ };
252
287
 
253
- // Update prices for filtered products
254
- lowStock.stateEach((product, productUpdater) => {
255
- productUpdater.price.update((price) => price * 0.8);
256
- });
288
+ return (
289
+ <div>
290
+ {/* Address tabs with validation indicators */}
291
+ <div className="flex space-x-2 mt-2">
292
+ {user.addresses.stateMap((_, setter, index) => {
293
+ const errorCount = setter.showValidationErrors().length;
294
+ return (
295
+ <button
296
+ key={index}
297
+ onClick={() => setCurrentAddressIndex(index)}
298
+ className={`rounded-lg flex items-center justify-center ${
299
+ errorCount > 0
300
+ ? "border-red-500 bg-red-400"
301
+ : currentAddressIndex === index
302
+ ? "bg-blue-500 text-white"
303
+ : "bg-gray-200"
304
+ }`}
305
+ >
306
+ {index + 1}
307
+ {errorCount > 0 && (
308
+ <div className="bg-red-500 text-white rounded-full">
309
+ {errorCount}
310
+ </div>
311
+ )}
312
+ </button>
313
+ );
314
+ })}
315
+ <button onClick={addNewAddress}>Add</button>
316
+ </div>
317
+
318
+ {/* Current address form */}
319
+ {user.addresses.get().length > 0 && (
320
+ <div className="grid grid-cols-1 gap-4">
321
+ {/* Access fields with index() method */}
322
+ {user.addresses.index(currentAddressIndex).street.formElement(
323
+ (params) => (
324
+ <div>
325
+ <label>Street</label>
326
+ <input value={params.get()} onChange={(e) => params.set(e.target.value)} />
327
+ </div>
328
+ ),
329
+ {
330
+ validation: {
331
+ message: "Street address is required"
332
+ }
333
+ }
334
+ )}
335
+
336
+ {/* City and State in a row */}
337
+ <div className="grid grid-cols-2 gap-4">
338
+ {user.addresses.index(currentAddressIndex).city.formElement((params) => (
339
+ <div>
340
+ <label>City</label>
341
+ <input {...params.inputProps} />
342
+ </div>
343
+ ))}
344
+
345
+ {user.addresses.index(currentAddressIndex).state.formElement((params) => (
346
+ <div>
347
+ <label>State</label>
348
+ <input {...params.inputProps} />
349
+ </div>
350
+ ))}
351
+ </div>
352
+
353
+ {/* Boolean field handling */}
354
+ {user.addresses.index(currentAddressIndex).isDefault.formElement((params) => (
355
+ <div className="flex items-center">
356
+ <input
357
+ type="checkbox"
358
+ checked={params.get()}
359
+ onChange={(e) => params.set(e.target.checked)}
360
+ id={`default-address-${currentAddressIndex}`}
361
+ />
362
+ <label htmlFor={`default-address-${currentAddressIndex}`}>
363
+ Set as default address
364
+ </label>
365
+ </div>
366
+ ))}
367
+
368
+ {/* Remove address button */}
369
+ {user.addresses.get().length > 1 && (
370
+ <button
371
+ onClick={() => {
372
+ user.addresses.cut(currentAddressIndex);
373
+ setCurrentAddressIndex(Math.max(0, currentAddressIndex - 1));
374
+ }}
375
+ >
376
+ Remove Selected Address
377
+ </button>
378
+ )}
379
+ </div>
380
+ )}
381
+ </div>
382
+ );
383
+ }
257
384
  ```
258
385
 
259
- #### `.stateFlattenOn()`
386
+ ### Form Actions
260
387
 
261
- Flattens nested arrays by property.
388
+ Cogsbox provides methods to manage form state:
262
389
 
263
390
  ```typescript
264
- // Get all variants across products
265
- const allVariants = updater.catalog.products.stateFlattenOn("variants");
266
- ```
267
-
268
- ### Form Integration
391
+ // Reset form to initial state
392
+ const handleReset = () => {
393
+ user.revertToInitialState();
394
+ };
269
395
 
270
- #### `.formElement()`
396
+ // Validate all fields using Zod schema
397
+ const handleSubmit = () => {
398
+ if (user.validateZodSchema()) {
399
+ // All valid, proceed with submission
400
+ submitData(user.get());
401
+ }
402
+ };
403
+ ```
271
404
 
272
- Creates form control with validation.
405
+ ### Setting Up Zod Validation
273
406
 
274
407
  ```typescript
275
- updater.cart.shipping.address.formElement("shipping", ({ inputProps }) => (
276
- <input {...inputProps} placeholder="Shipping address" />
277
- ), {
278
- validation: {
279
- message: "Address is required",
280
- onChange: { clear: ["shipping.address"] }
408
+ // Setting up validation at initialization
409
+ export const { useCogsState } = createCogsState({
410
+ user: {
411
+ initialState: {
412
+ firstName: "",
413
+ lastName: "",
414
+ email: "",
415
+ phone: "",
416
+ addresses: [
417
+ {
418
+ street: "",
419
+ city: "",
420
+ state: "",
421
+ zipCode: "",
422
+ country: "USA",
423
+ isDefault: false,
424
+ },
425
+ ],
426
+ },
427
+ validation: {
428
+ key: "userForm", // Used for error tracking
429
+ zodSchema: z.object({
430
+ firstName: z.string().min(1, "First name is required"),
431
+ lastName: z.string().min(1, "Last name is required"),
432
+ email: z.string().email("Please enter a valid email"),
433
+ phone: z.string().min(10, "Phone number must be at least 10 digits"),
434
+ addresses: z.array(
435
+ z.object({
436
+ street: z.string().min(1, "Street is required"),
437
+ city: z.string().min(1, "City is required"),
438
+ state: z.string().min(1, "State is required"),
439
+ zipCode: z
440
+ .string()
441
+ .min(5, "Zip code must be at least 5 characters"),
442
+ country: z.string(),
443
+ isDefault: z.boolean(),
444
+ })
445
+ ),
446
+ }),
447
+ },
281
448
  },
282
- debounceTime: 300
283
- })
449
+ });
284
450
  ```
285
451
 
286
- ### Menu & Selection API
287
-
288
- #### `.setSelected()`
289
-
290
- Marks an item as selected in a list.
452
+ ## Server Synchronization
291
453
 
292
454
  ```typescript
293
- // Select product
294
- updater.catalog.products[0].setSelected(true);
455
+ // Setting up server sync
456
+ const products = useCogsState("products", {
457
+ serverSync: {
458
+ syncKey: "products",
459
+ syncFunction: ({ state }) => api.updateProducts(state),
460
+ debounce: 1000, // ms
461
+ mutation: useMutation(api.updateProducts),
462
+ },
463
+ });
295
464
 
296
- // Select category
297
- updater.catalog.categories.findWith("name", "sports").setSelected(true);
465
+ // State is automatically synced with server after changes
466
+ products.items[0].stock.update((prev) => prev - 1);
298
467
  ```
299
468
 
300
- #### `.getSelected()`
301
-
302
- Gets currently selected item from a list.
469
+ ## Local Storage Persistence
303
470
 
304
471
  ```typescript
305
- const selectedProduct = updater.catalog.products.getSelected();
472
+ // Automatically save state to localStorage
473
+ const cart = useCogsState("cart", {
474
+ localStorage: {
475
+ key: "shopping-cart",
476
+ },
477
+ });
306
478
  ```
307
479
 
308
- [Rest of documentation remains the same...]
309
-
310
- ## Examples
311
-
312
- ### Product Catalog Management
480
+ ## Example: Shopping Cart
313
481
 
314
482
  ```typescript
315
- function ProductList() {
316
- const [state, updater] = useCogsState("catalog", {
317
- serverSync: {
318
- syncKey: "products",
319
- syncFunction: async ({ state }) => {
320
- await api.updateProducts(state.products)
321
- }
322
- }
323
- });
324
-
325
- const filteredProducts = updater.products
326
- .stateFilter(product =>
327
- product.price >= state.filters.priceRange.min &&
328
- product.price <= state.filters.priceRange.max &&
329
- (state.filters.selectedCategories.length === 0 ||
330
- product.categories.some(c => state.filters.selectedCategories.includes(c)))
483
+ function ShoppingCart() {
484
+ const cart = useCogsState("cart");
485
+ const products = useCogsState("products");
486
+
487
+ const addToCart = (productId) => {
488
+ const product = products.items.findWith("id", productId).get();
489
+
490
+ cart.items.uniqueInsert(
491
+ {
492
+ productId,
493
+ name: product.name,
494
+ price: product.price,
495
+ quantity: 1
496
+ },
497
+ ["productId"],
498
+ // If product exists, update quantity instead
499
+ (existingItem) => ({
500
+ ...existingItem,
501
+ quantity: existingItem.quantity + 1
502
+ })
331
503
  );
332
504
 
505
+ // Update total
506
+ cart.total.update(prev => prev + product.price);
507
+ };
508
+
333
509
  return (
334
510
  <div>
335
- {filteredProducts.stateEach((product, productUpdater) => (
336
- <ProductCard
337
- key={product.id}
338
- product={product}
339
- onUpdateStock={(variantId, newStock) =>
340
- productUpdater
341
- .variants
342
- .findWith("id", variantId)
343
- .stock
344
- .update(newStock)
345
- }
346
- />
347
- ))}
348
- </div>
349
- );
350
- }
351
- ```
511
+ <h2>Your Cart</h2>
352
512
 
353
- ### Shopping Cart Management
513
+ {cart.items.stateMap((item, itemUpdater) => (
514
+ <div key={item.productId} className="cart-item">
515
+ <div>{item.name}</div>
516
+ <div>${item.price}</div>
354
517
 
355
- ```typescript
356
- function CartManager() {
357
- const [state, updater] = useCogsState("cart", {
358
- localStorage: {
359
- key: "shopping-cart"
360
- }
361
- });
362
-
363
- const addToCart = (product: Product, variantId: string) => {
364
- updater.items.uniqueInsert({
365
- productId: product.id,
366
- variantId,
367
- quantity: 1
368
- }, ['productId', 'variantId']);
369
- };
518
+ <div className="quantity">
519
+ <button onClick={() =>
520
+ itemUpdater.quantity.update(prev => Math.max(prev - 1, 0))
521
+ }>-</button>
370
522
 
371
- return (
372
- <div>
373
- {updater.items.stateEach((item, itemUpdater) => (
374
- <CartItem
375
- key={item.productId}
376
- item={item}
377
- onUpdateQuantity={qty => itemUpdater.quantity.update(qty)}
378
- onRemove={() => itemUpdater.cut()}
379
- />
523
+ <span>{item.quantity}</span>
524
+
525
+ <button onClick={() =>
526
+ itemUpdater.quantity.update(prev => prev + 1)
527
+ }>+</button>
528
+ </div>
529
+
530
+ <button onClick={() => itemUpdater.cut()}>Remove</button>
531
+ </div>
380
532
  ))}
533
+
534
+ <div className="cart-total">
535
+ <strong>Total:</strong> ${cart.total.get()}
536
+ </div>
381
537
  </div>
382
538
  );
383
539
  }
384
540
  ```
385
541
 
386
- [Rest of TypeScript section remains the same...]
542
+ ## Common Patterns and Tips
543
+
544
+ 1. **Path-based Updates**: Always use the fluent API to update nested properties.
545
+
546
+ ```typescript
547
+ // Good
548
+ updater.users[0].address.city.update("New York");
549
+
550
+ // Avoid
551
+ updater.update({ ...state, users: [...] });
552
+ ```
553
+
554
+ 2. **Working with Arrays**: Use the built-in array methods instead of manually updating array state.
555
+
556
+ ```typescript
557
+ // Good
558
+ updater.users.insert(newUser);
559
+ updater.users.findWith("id", 123).active.update(true);
560
+
561
+ // Avoid
562
+ updater.users.update([...users, newUser]);
563
+ ```
564
+
565
+ 3. **Optimization**: Use the appropriate reactivity type for your needs.
566
+
567
+ ```typescript
568
+ // For lists where only specific items change frequently
569
+ updater.items.stateMap((item) => (
570
+ <div>{item.$get()}</div> // Only this item re-renders when changed
571
+ ));
572
+ ```
573
+
574
+ 4. **Form Management**: Use formElement for all form inputs to get automatic validation and debouncing.
575
+ ```typescript
576
+ profile.name.formElement(
577
+ ({ inputProps }) => <input {...inputProps} />,
578
+ { debounceTime: 300 }
579
+ );
580
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogsbox-state",
3
- "version": "0.5.7",
3
+ "version": "0.5.8",
4
4
  "description": "React state management library with form controls and server sync",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",