@fun-tools/store 1.0.1 → 1.0.2

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.
package/README.md CHANGED
@@ -1,174 +1,229 @@
1
1
  # @fun-tools/store
2
2
 
3
- > A lightweight, type-safe external store library for React, React Native, and Next.js
3
+ > A simple and lightweight state management library for React apps
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@fun-tools/store.svg)](https://www.npmjs.com/package/@fun-tools/store)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
7
 
8
- ## 📋 Overview
8
+ ## 📋 What is @fun-tools/store?
9
9
 
10
- `@fun-tools/store` is a minimal yet powerful state management solution built on React's `useSyncExternalStore` API. It provides automatic handler generation, TypeScript support, and works seamlessly across React, React Native, and Next.js applications.
10
+ `@fun-tools/store` is an easy-to-use state management library for React. Think of it as a smarter way to share data between your components without the complexity of Redux or other heavy tools.
11
11
 
12
- ## Features
12
+ **Perfect for beginners and experienced developers alike!**
13
13
 
14
- - **🎯 Type-Safe**: Full TypeScript support with intelligent type inference
15
- - **⚡ Lightweight**: Minimal bundle size with zero dependencies (except React peer dependency)
16
- - **🔄 Auto-Generated Handlers**: Automatic CRUD operations for arrays, objects, and primitives
17
- - **🪝 Custom Handlers**: Define your own sync and async state handlers
18
- - **🎨 Flexible API**: Use as a global store or scoped with React Context
19
- - **📦 Tree-Shakeable**: Only bundle what you use
20
- - **🚀 Performance Optimized**: Built-in shallow equality checks and snapshot caching
14
+ ## Why Choose @fun-tools/store?
15
+
16
+ - **Super Easy to Learn** - Get started in minutes, not hours
17
+ - **Automatic Features** - Get built-in functions for free (no need to write repetitive code)
18
+ - **TypeScript Friendly** - Get helpful suggestions as you type
19
+ - **Very Small** - Won't bloat your app size
20
+ - **Fast Performance** - Components only update when they need to
21
+ - ✅ **Works Everywhere** - React, React Native, and Next.js
21
22
 
22
23
  ## 📦 Installation
23
24
 
24
- ```bash
25
- npm install @fun-tools/store
26
- ```
25
+ Choose your favorite package manager:
27
26
 
28
27
  ```bash
29
- yarn add @fun-tools/store
28
+ npm install @fun-tools/store
30
29
  ```
31
30
 
32
- ```bash
33
- pnpm add @fun-tools/store
34
- ```
31
+ ## 🚀 Quick Start - Your First Store
35
32
 
36
- ## 🚀 Quick Start
33
+ Let's create a simple counter in 3 easy steps:
37
34
 
38
- ### Basic Usage (Global Store)
35
+ ### Step 1: Create Your Store
39
36
 
40
37
  ```tsx
41
- import { createStore } from "@ex/store";
38
+ import { createStore } from "@fun-tools/store";
42
39
 
43
- // Define your store
44
- const store = createStore({
40
+ // Create a store with initial values
41
+ const counterStore = createStore({
45
42
  states: {
46
- count: 0,
47
- user: { name: "John", age: 25 },
48
- items: ["apple", "banana"],
43
+ count: 0, // Our counter starts at 0
49
44
  },
50
45
  });
46
+ ```
51
47
 
52
- // Use in components
48
+ ### Step 2: Use It in a Component
49
+
50
+ ```tsx
53
51
  function Counter() {
54
- const { count } = store.useStore((state) => ({ count: state.count }));
55
- const handlers = store.useHandlers();
52
+ // Get the count value
53
+ const count = counterStore.useStore((state) => state.count);
54
+
55
+ // Get the handlers (functions to change the state)
56
+ const handlers = counterStore.useHandlers();
56
57
 
57
58
  return (
58
59
  <div>
59
- <p>Count: {count}</p>
60
+ <h1>Count: {count}</h1>
61
+ {/* Set count to a specific number */}
62
+ <button onClick={() => handlers.count.set(10)}>Set to 10</button>
63
+ {/* Increment using current value */}
60
64
  <button onClick={() => handlers.count.set((prev) => prev + 1)}>
61
- Increment
65
+ Add 1
62
66
  </button>
67
+ {/* Reset to initial value (0) */}
63
68
  <button onClick={() => handlers.count.reset()}>Reset</button>
64
69
  </div>
65
70
  );
66
71
  }
67
72
  ```
68
73
 
69
- ### Context-Based Store (Scoped)
74
+ That's it! You have a working counter. 🎉
70
75
 
71
- ```tsx
72
- import { createStoreProvider } from "@ex/store";
76
+ ## 📖 Core Concepts
73
77
 
74
- // Create provider
75
- const { Provider, useStore, useHandlers } = createStoreProvider({
78
+ ### 1. Creating a Store
79
+
80
+ A store is where you keep your app's data. It's like a box that holds all your values.
81
+
82
+ ```tsx
83
+ const myStore = createStore({
76
84
  states: {
77
- theme: "light",
78
- settings: { notifications: true },
85
+ // Put all your data here
86
+ userName: "John",
87
+ age: 25,
88
+ isLoggedIn: false,
79
89
  },
80
90
  });
91
+ ```
81
92
 
82
- // Wrap your app
83
- function App() {
84
- return (
85
- <Provider>
86
- <ThemeToggle />
87
- </Provider>
88
- );
89
- }
93
+ ### 2. Reading Data from the Store
90
94
 
91
- // Use in child components
92
- function ThemeToggle() {
93
- const { theme } = useStore((state) => ({ theme: state.theme }));
94
- const handlers = useHandlers();
95
+ Use `useStore` to read data in your components:
96
+
97
+ ```tsx
98
+ function MyComponent() {
99
+ // Method 1: Get one value
100
+ const userName = myStore.useStore((state) => state.userName);
101
+
102
+ // Method 2: Get multiple values
103
+ const { userName, age } = myStore.useStore((state) => ({
104
+ userName: state.userName,
105
+ age: state.age,
106
+ }));
95
107
 
96
108
  return (
97
- <button
98
- onClick={() => handlers.theme.set(theme === "light" ? "dark" : "light")}
99
- >
100
- Current: {theme}
101
- </button>
109
+ <div>
110
+ Hello, {userName}! You are {age} years old.
111
+ </div>
102
112
  );
103
113
  }
104
114
  ```
105
115
 
106
- ## 📖 API Reference
116
+ ### 3. Changing Data (Using Handlers)
107
117
 
108
- ### `createStore(config)`
118
+ Handlers are functions that change your data. The library creates them automatically!
109
119
 
110
- Creates a global store instance.
120
+ ```tsx
121
+ function MyComponent() {
122
+ const handlers = myStore.useHandlers();
111
123
 
112
- **Parameters:**
124
+ // Change the userName
125
+ handlers.userName.set("Jane");
113
126
 
114
- - `states`: Initial state object
115
- - `syncHandlers?`: Custom synchronous handlers (optional)
116
- - `asyncHandlers?`: Custom asynchronous handlers (optional)
127
+ // Reset to initial value
128
+ handlers.userName.reset();
129
+ }
130
+ ```
117
131
 
118
- **Returns:**
132
+ ## 🎨 Auto-Generated Handlers
119
133
 
120
- - `useStore<T>(selector)`: Hook to select and subscribe to state
121
- - `useHandlers()`: Hook to access all handlers
134
+ The best part? You get **FREE handlers** based on your data type!
122
135
 
123
- ### `createStoreProvider(config)`
136
+ ### For Simple Values (String, Number)
124
137
 
125
- Creates a context-based store provider.
138
+ ```tsx
139
+ const store = createStore({
140
+ states: {
141
+ name: "John",
142
+ age: 25,
143
+ },
144
+ });
126
145
 
127
- **Parameters:**
146
+ const handlers = store.useHandlers();
128
147
 
129
- - Same as `createStore`
148
+ // Set to a new value
149
+ handlers.name.set("Jane");
150
+ handlers.age.set(26);
130
151
 
131
- **Returns:**
152
+ // ✅ Set using current value
153
+ handlers.age.set((currentAge) => currentAge + 1);
132
154
 
133
- - `Provider`: React component to wrap your app
134
- - `useStore<T>(selector)`: Hook to select and subscribe to state
135
- - `useHandlers()`: Hook to access all handlers
155
+ // Reset to initial value
156
+ handlers.name.reset(); // Back to "John"
157
+ handlers.age.reset(); // Back to 25
158
+ ```
136
159
 
137
- ## 🎨 Auto-Generated Handlers
160
+ ### For Boolean (True/False)
138
161
 
139
- The library automatically generates handlers based on your state types:
162
+ ```tsx
163
+ const store = createStore({
164
+ states: {
165
+ isOpen: false,
166
+ isDarkMode: true,
167
+ },
168
+ });
140
169
 
141
- ### For All Types
170
+ const handlers = store.useHandlers();
142
171
 
143
- ```tsx
144
- // Set value (supports callbacks)
145
- handlers.count.set(10);
146
- handlers.count.set((prev) => prev + 1);
172
+ // ✅ Toggle (switch between true/false)
173
+ handlers.isOpen.toggle();
174
+
175
+ // Set to specific value
176
+ handlers.isDarkMode.set(false);
147
177
 
148
- // Reset to initial value
149
- handlers.count.reset();
178
+ // Reset to initial value
179
+ handlers.isOpen.reset();
150
180
  ```
151
181
 
152
- ### For Arrays
182
+ ### For Arrays (Lists)
153
183
 
154
184
  ```tsx
155
185
  const store = createStore({
156
- states: { items: ["a", "b", "c"] },
186
+ states: {
187
+ fruits: ["apple", "banana"],
188
+ numbers: [1, 2, 3],
189
+ },
157
190
  });
158
191
 
159
192
  const handlers = store.useHandlers();
160
193
 
161
- // Add items
162
- handlers.items.push("d");
163
- handlers.items.unshift("z");
194
+ // Add to end
195
+ handlers.fruits.push("orange");
196
+ // Result: ["apple", "banana", "orange"]
197
+
198
+ // ✅ Add to beginning
199
+ handlers.fruits.unShift("mango");
200
+ // Result: ["mango", "apple", "banana", "orange"]
201
+
202
+ // ✅ Remove from end
203
+ handlers.fruits.pop();
204
+ // Result: ["mango", "apple", "banana"]
205
+
206
+ // ✅ Remove from beginning
207
+ handlers.fruits.shift();
208
+ // Result: ["apple", "banana"]
209
+
210
+ // ✅ Update item at specific position
211
+ handlers.fruits.update(0, "grape");
212
+ // Result: ["grape", "banana"]
213
+
214
+ // ✅ Update item using current value
215
+ handlers.numbers.update(1, (current) => current * 2);
164
216
 
165
- // Remove items
166
- handlers.items.pop();
167
- handlers.items.shift();
217
+ // Remove item at specific position
218
+ handlers.fruits.remove(1);
219
+ // Result: ["grape"]
168
220
 
169
- // Transform
170
- handlers.items.filter((item) => item !== "b");
171
- handlers.items.map((item) => item.toUpperCase());
221
+ // ✅ Set entire array
222
+ handlers.fruits.set(["kiwi", "melon"]);
223
+
224
+ // ✅ Reset to initial value
225
+ handlers.fruits.reset();
226
+ // Result: ["apple", "banana"]
172
227
  ```
173
228
 
174
229
  ### For Objects
@@ -178,210 +233,284 @@ const store = createStore({
178
233
  states: {
179
234
  user: {
180
235
  name: "John",
181
- address: { city: "NYC", zip: "10001" },
236
+ email: "john@example.com",
237
+ settings: {
238
+ theme: "light",
239
+ notifications: true,
240
+ },
182
241
  },
183
242
  },
184
243
  });
185
244
 
186
245
  const handlers = store.useHandlers();
187
246
 
188
- // Update single nested property
247
+ // Update single property
189
248
  handlers.user.update("name", "Jane");
190
- handlers.user.update("address.city", "LA");
191
249
 
192
- // Update multiple properties (deep merge)
250
+ // Update with current value
251
+ handlers.user.update("name", (currentName) => currentName.toUpperCase());
252
+
253
+ // ✅ Update nested property (use dot notation)
254
+ handlers.user.update("settings.theme", "dark");
255
+
256
+ // ✅ Update multiple properties at once
193
257
  handlers.user.updateMany({
194
258
  name: "Jane",
195
- address: { city: "LA" },
259
+ email: "jane@example.com",
260
+ });
261
+
262
+ // ✅ Update nested properties
263
+ handlers.user.updateMany({
264
+ settings: {
265
+ theme: "dark",
266
+ },
267
+ });
268
+
269
+ // ✅ Set entire object
270
+ handlers.user.set({
271
+ name: "Bob",
272
+ email: "bob@example.com",
273
+ settings: { theme: "blue", notifications: false },
196
274
  });
275
+
276
+ // ✅ Reset to initial value
277
+ handlers.user.reset();
197
278
  ```
198
279
 
199
280
  ## 🔧 Custom Handlers
200
281
 
201
- ### Sync Handlers
282
+ Sometimes you need custom logic. Create your own handlers!
283
+
284
+ ### Sync Handlers (Instant Changes)
202
285
 
203
286
  ```tsx
204
287
  const store = createStore({
205
288
  states: {
206
289
  count: 0,
290
+ firstName: "John",
291
+ lastName: "Doe",
207
292
  },
293
+
294
+ // Define your custom handlers here
208
295
  syncHandlers: {
209
- increment: (states) => {
210
- states.count++;
296
+ // Handler with no parameters
297
+ increment: (state) => {
298
+ state.count = state.count + 1;
211
299
  },
212
- incrementBy: (states, amount: number) => {
213
- states.count += amount;
300
+
301
+ // Handler with parameters
302
+ incrementBy: (state, amount: number) => {
303
+ state.count = state.count + amount;
304
+ },
305
+
306
+ // Handler that changes multiple values
307
+ setFullName: (state, first: string, last: string) => {
308
+ state.firstName = first;
309
+ state.lastName = last;
214
310
  },
215
311
  },
216
312
  });
217
313
 
218
- // Usage
219
- const handlers = store.useHandlers();
220
- handlers.increment(); // count + 1
221
- handlers.incrementBy(5); // count + 5
314
+ // Use them in components
315
+ function MyComponent() {
316
+ const handlers = store.useHandlers();
317
+
318
+ return (
319
+ <div>
320
+ <button onClick={() => handlers.increment()}>Add 1</button>
321
+ <button onClick={() => handlers.incrementBy(5)}>Add 5</button>
322
+ <button onClick={() => handlers.setFullName("Jane", "Smith")}>
323
+ Change Name
324
+ </button>
325
+ </div>
326
+ );
327
+ }
222
328
  ```
223
329
 
224
- ### Async Handlers
330
+ ### Async Handlers (For API Calls)
331
+
332
+ Perfect for fetching data from servers!
225
333
 
226
334
  ```tsx
227
335
  const store = createStore({
228
336
  states: {
229
337
  user: null,
230
338
  loading: false,
339
+ error: null,
231
340
  },
341
+
232
342
  asyncHandlers: {
233
- fetchUser: async (states, userId: string) => {
234
- states.loading = true;
235
- const response = await fetch(`/api/users/${userId}`);
236
- states.user = await response.json();
237
- states.loading = false;
343
+ // Fetch user from API
344
+ fetchUser: async (state, userId: string) => {
345
+ // Set loading to true
346
+ state.loading = true;
347
+ state.error = null;
348
+
349
+ try {
350
+ // Fetch from API
351
+ const response = await fetch(`https://api.example.com/users/${userId}`);
352
+ const data = await response.json();
353
+
354
+ // Update state with data
355
+ state.user = data;
356
+ } catch (err) {
357
+ // Handle errors
358
+ state.error = "Failed to fetch user";
359
+ } finally {
360
+ // Set loading to false
361
+ state.loading = false;
362
+ }
238
363
  },
239
364
  },
240
365
  });
241
366
 
242
- // Usage
243
- const handlers = store.useHandlers();
244
- await handlers.fetchUser("123");
245
- ```
246
-
247
- ## 💡 Advanced Usage
248
-
249
- ### Selective Subscriptions
250
-
251
- The `useStore` selector ensures components only re-render when selected state changes:
252
-
253
- ```tsx
254
- // Component only re-renders when 'count' changes
255
- function CountDisplay() {
256
- const { count } = store.useStore((state) => ({ count: state.count }));
257
- return <div>{count}</div>;
258
- }
259
-
260
- // Component only re-renders when 'user.name' changes
261
- function UserName() {
262
- const { name } = store.useStore((state) => ({
263
- name: state.user.name,
367
+ // Use in component
368
+ function UserProfile() {
369
+ const { user, loading } = store.useStore((state) => ({
370
+ user: state.user,
371
+ loading: state.loading,
264
372
  }));
265
- return <div>{name}</div>;
373
+ const handlers = store.useHandlers();
374
+
375
+ return (
376
+ <div>
377
+ <button onClick={() => handlers.fetchUser("123")}>Load User</button>
378
+ {loading && <p>Loading...</p>}
379
+ {user && <p>Name: {user.name}</p>}
380
+ </div>
381
+ );
266
382
  }
267
383
  ```
268
384
 
269
- ### Multiple Stores
385
+ ## 🎁 Using Providers (Scoped Stores)
270
386
 
271
- You can create multiple independent stores:
387
+ Sometimes you want a store that only works within a specific part of your app. Use `createStoreProvider`!
272
388
 
273
389
  ```tsx
274
- const authStore = createStore({
275
- states: { user: null, isAuthenticated: false },
276
- });
390
+ import { createStoreProvider } from "@fun-tools/store";
277
391
 
278
- const cartStore = createStore({
279
- states: { items: [], total: 0 },
392
+ // Create a provider
393
+ const { Provider, useStore, useHandlers } = createStoreProvider({
394
+ states: {
395
+ theme: "light",
396
+ language: "en",
397
+ },
280
398
  });
281
399
 
400
+ // Wrap part of your app
282
401
  function App() {
283
- const { user } = authStore.useStore((state) => ({ user: state.user }));
284
- const { items } = cartStore.useStore((state) => ({ items: state.items }));
285
-
286
- // ...
402
+ return (
403
+ <Provider>
404
+ <Header />
405
+ <Content />
406
+ </Provider>
407
+ );
287
408
  }
288
- ```
289
-
290
- ### TypeScript Type Inference
291
-
292
- The library provides full type safety with intelligent inference:
293
409
 
294
- ```tsx
295
- const store = createStore({
296
- states: {
297
- count: 0,
298
- user: { name: "John", age: 25 },
299
- },
300
- syncHandlers: {
301
- setUserAge: (states, age: number) => {
302
- states.user.age = age;
303
- },
304
- },
305
- });
410
+ // Use in any child component
411
+ function Header() {
412
+ const theme = useStore((state) => state.theme);
413
+ const handlers = useHandlers();
306
414
 
307
- const handlers = store.useHandlers();
415
+ return (
416
+ <button
417
+ onClick={() => handlers.theme.set(theme === "light" ? "dark" : "light")}
418
+ >
419
+ Current theme: {theme}
420
+ </button>
421
+ );
422
+ }
423
+ ```
308
424
 
309
- // ✅ TypeScript knows the types
310
- handlers.count.set(10); // ✓
311
- handlers.user.update("name", "Jane"); // ✓
312
- handlers.setUserAge(30); // ✓
425
+ **The difference:**
313
426
 
314
- // TypeScript catches errors
315
- handlers.count.set("invalid"); // Error: Argument of type 'string' is not assignable
316
- handlers.setUserAge("invalid"); // Error: Argument of type 'string' is not assignable
317
- ```
427
+ - `createStore` = Global (available everywhere)
428
+ - `createStoreProvider` = Scoped (only available inside `<Provider>`)
318
429
 
319
- ## 🏗️ Architecture
430
+ ## 💡 Performance Tips
320
431
 
321
- The library uses React's `useSyncExternalStore` API for optimal performance and compatibility:
432
+ ### Only Re-render When Needed
322
433
 
323
- - **Snapshot Caching**: Prevents unnecessary re-renders with shallow equality checks
324
- - **WeakMap Storage**: Efficient snapshot management with automatic garbage collection
325
- - **Immutable Updates**: State changes create new references for proper React updates
326
- - **Subscription Management**: Automatic cleanup when components unmount
434
+ Components only re-render when the data they use changes:
327
435
 
328
- ## 🎯 Use Cases
436
+ ```tsx
437
+ // ❌ BAD: Component re-renders on ANY state change
438
+ const allState = store.useStore((state) => state);
329
439
 
330
- Perfect for:
440
+ // ✅ GOOD: Component only re-renders when count changes
441
+ const count = store.useStore((state) => state.count);
331
442
 
332
- -Small to medium-sized applications
333
- - Quick prototyping and MVPs
334
- - ✅ React Native mobile apps
335
- - ✅ Next.js applications (SSR compatible)
336
- - ✅ Projects needing simple global state
337
- - ✅ Teams wanting minimal state management setup
443
+ //GOOD: Component only re-renders when name or age change
444
+ const { name, age } = store.useStore((state) => ({
445
+ name: state.name,
446
+ age: state.age,
447
+ }));
448
+ ```
338
449
 
339
- ## 📝 Examples
450
+ ## 📚 Real-World Examples
340
451
 
341
- ### Todo App
452
+ ### Example 1: Todo App
342
453
 
343
454
  ```tsx
344
455
  const todoStore = createStore({
345
456
  states: {
346
- todos: [] as Array<{ id: string; text: string; completed: boolean }>,
457
+ todos: [] as Array<{ id: number; text: string; done: boolean }>,
347
458
  },
459
+
348
460
  syncHandlers: {
349
- addTodo: (states, text: string) => {
350
- states.todos.push({
351
- id: Date.now().toString(),
352
- text,
353
- completed: false,
461
+ addTodo: (state, text: string) => {
462
+ state.todos.push({
463
+ id: Date.now(),
464
+ text: text,
465
+ done: false,
354
466
  });
355
467
  },
356
- toggleTodo: (states, id: string) => {
357
- const todo = states.todos.find((t) => t.id === id);
358
- if (todo) todo.completed = !todo.completed;
468
+
469
+ toggleTodo: (state, id: number) => {
470
+ const todo = state.todos.find((t) => t.id === id);
471
+ if (todo) {
472
+ todo.done = !todo.done;
473
+ }
359
474
  },
360
- removeTodo: (states, id: string) => {
361
- states.todos = states.todos.filter((t) => t.id !== id);
475
+
476
+ deleteTodo: (state, id: number) => {
477
+ state.todos = state.todos.filter((t) => t.id !== id);
362
478
  },
363
479
  },
364
480
  });
365
481
 
366
- function TodoList() {
367
- const { todos } = todoStore.useStore((state) => ({ todos: state.todos }));
482
+ function TodoApp() {
483
+ const todos = todoStore.useStore((state) => state.todos);
368
484
  const handlers = todoStore.useHandlers();
485
+ const [input, setInput] = React.useState("");
369
486
 
370
487
  return (
371
488
  <div>
489
+ <input
490
+ value={input}
491
+ onChange={(e) => setInput(e.target.value)}
492
+ placeholder="Add a todo..."
493
+ />
494
+ <button
495
+ onClick={() => {
496
+ handlers.addTodo(input);
497
+ setInput("");
498
+ }}
499
+ >
500
+ Add
501
+ </button>
502
+
372
503
  {todos.map((todo) => (
373
504
  <div key={todo.id}>
374
505
  <input
375
506
  type="checkbox"
376
- checked={todo.completed}
507
+ checked={todo.done}
377
508
  onChange={() => handlers.toggleTodo(todo.id)}
378
509
  />
379
- <span
380
- style={{ textDecoration: todo.completed ? "line-through" : "none" }}
381
- >
510
+ <span style={{ textDecoration: todo.done ? "line-through" : "none" }}>
382
511
  {todo.text}
383
512
  </span>
384
- <button onClick={() => handlers.removeTodo(todo.id)}>Delete</button>
513
+ <button onClick={() => handlers.deleteTodo(todo.id)}>Delete</button>
385
514
  </div>
386
515
  ))}
387
516
  </div>
@@ -389,49 +518,180 @@ function TodoList() {
389
518
  }
390
519
  ```
391
520
 
392
- ### Form Management
521
+ ### Example 2: Shopping Cart
393
522
 
394
523
  ```tsx
395
- const formStore = createStore({
524
+ const cartStore = createStore({
396
525
  states: {
397
- formData: {
398
- username: "",
399
- email: "",
400
- bio: "",
401
- },
402
- errors: {} as Record<string, string>,
526
+ items: [] as Array<{
527
+ id: number;
528
+ name: string;
529
+ price: number;
530
+ quantity: number;
531
+ }>,
532
+ total: 0,
403
533
  },
534
+
404
535
  syncHandlers: {
405
- updateField: (
406
- states,
407
- { field, value }: { field: string; value: string }
408
- ) => {
409
- states.formData = { ...states.formData, [field]: value };
536
+ addItem: (state, product: { id: number; name: string; price: number }) => {
537
+ // Check if item already exists
538
+ const existing = state.items.find((item) => item.id === product.id);
539
+
540
+ if (existing) {
541
+ // Increase quantity
542
+ existing.quantity++;
543
+ } else {
544
+ // Add new item
545
+ state.items.push({ ...product, quantity: 1 });
546
+ }
547
+
548
+ // Update total
549
+ state.total = state.items.reduce(
550
+ (sum, item) => sum + item.price * item.quantity,
551
+ 0
552
+ );
553
+ },
554
+
555
+ removeItem: (state, id: number) => {
556
+ state.items = state.items.filter((item) => item.id !== id);
557
+ state.total = state.items.reduce(
558
+ (sum, item) => sum + item.price * item.quantity,
559
+ 0
560
+ );
561
+ },
562
+
563
+ clearCart: (state) => {
564
+ state.items = [];
565
+ state.total = 0;
410
566
  },
411
- validate: (states) => {
412
- const errors: Record<string, string> = {};
413
- if (!states.formData.username) errors.username = "Required";
414
- if (!states.formData.email.includes("@")) errors.email = "Invalid email";
415
- states.errors = errors;
567
+ },
568
+ });
569
+ ```
570
+
571
+ ### Example 3: User Authentication
572
+
573
+ ```tsx
574
+ const authStore = createStore({
575
+ states: {
576
+ user: null as { id: string; name: string; email: string } | null,
577
+ isAuthenticated: false,
578
+ isLoading: false,
579
+ },
580
+
581
+ asyncHandlers: {
582
+ login: async (state, email: string, password: string) => {
583
+ state.isLoading = true;
584
+
585
+ try {
586
+ const response = await fetch("/api/login", {
587
+ method: "POST",
588
+ headers: { "Content-Type": "application/json" },
589
+ body: JSON.stringify({ email, password }),
590
+ });
591
+
592
+ const data = await response.json();
593
+
594
+ state.user = data.user;
595
+ state.isAuthenticated = true;
596
+ } catch (error) {
597
+ console.error("Login failed:", error);
598
+ } finally {
599
+ state.isLoading = false;
600
+ }
601
+ },
602
+
603
+ logout: async (state) => {
604
+ await fetch("/api/logout", { method: "POST" });
605
+ state.user = null;
606
+ state.isAuthenticated = false;
607
+ },
608
+ },
609
+ });
610
+ ```
611
+
612
+ ## 🎓 TypeScript Support
613
+
614
+ The library works great with TypeScript! You get autocomplete and type safety.
615
+
616
+ ### Defining State Types
617
+
618
+ ```tsx
619
+ // Define your state shape
620
+ type UserState = {
621
+ name: string;
622
+ age: number;
623
+ email: string;
624
+ };
625
+
626
+ const store = createStore({
627
+ states: {
628
+ count: 0,
629
+ user: {
630
+ name: "John",
631
+ age: 25,
632
+ email: "john@example.com",
633
+ } as UserState,
634
+ },
635
+
636
+ syncHandlers: {
637
+ // TypeScript knows the state type!
638
+ updateUser: (state, newUser: UserState) => {
639
+ state.user = newUser;
416
640
  },
417
641
  },
418
642
  });
643
+
644
+ // TypeScript will catch errors
645
+ const handlers = store.useHandlers();
646
+ handlers.updateUser({
647
+ name: "Jane",
648
+ age: 26,
649
+ // ❌ Error: missing 'email' property
650
+ });
419
651
  ```
420
652
 
421
- ## 🤝 Contributing
653
+ ## Common Questions
422
654
 
423
- Contributions are welcome! Please feel free to submit a Pull Request.
655
+ ### Q: When should I use a global store vs provider?
424
656
 
425
- ## 📄 License
657
+ **Use Global Store (`createStore`) when:**
658
+
659
+ - Data is needed across your entire app (like user auth, theme)
660
+ - You want simple setup without wrapping components
661
+
662
+ **Use Provider (`createStoreProvider`) when:**
663
+
664
+ - Data is only needed in a specific section
665
+ - You want better component isolation
666
+ - You're building reusable components
667
+
668
+ ### Q: How is this different from useState?
669
+
670
+ `useState` is great for local component state. Use `@fun-tools/store` when:
671
+
672
+ - Multiple components need the same data
673
+ - You want to avoid prop drilling
674
+ - You need more powerful update functions
675
+
676
+ ### Q: Can I use multiple stores?
677
+
678
+ Yes! Create as many stores as you need:
679
+
680
+ ```tsx
681
+ const userStore = createStore({ states: { user: null } });
682
+ const cartStore = createStore({ states: { items: [] } });
683
+ const themeStore = createStore({ states: { theme: "light" } });
684
+ ```
426
685
 
427
- MIT © [Your Name]
428
686
 
429
687
  ## 🔗 Links
430
688
 
431
689
  - [GitHub Repository](https://github.com/yourusername/ex-store)
432
- - [Issues](https://github.com/yourusername/ex-store/issues)
690
+ - [Report Issues](https://github.com/yourusername/ex-store/issues)
433
691
  - [NPM Package](https://www.npmjs.com/package/@fun-tools/store)
434
692
 
435
693
  ---
436
694
 
437
- Made with ❤️ by developers, for developers
695
+ **Made with ❤️ for developers who value simplicity by @Mustak24**
696
+
697
+ Happy coding! 🚀