react-state-custom 1.0.18 → 1.0.19
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 +267 -89
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,142 +1,320 @@
|
|
|
1
|
-
# React State Custom
|
|
1
|
+
# React State Custom
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Simple. Powerful. TypeScript-first.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- **[API_DOCUMENTATION.md](./API_DOCUMENTATION.md)** - Complete API reference with examples for all exported functions, hooks, and classes
|
|
8
|
-
|
|
9
|
-
## 🚀 Quick Start
|
|
5
|
+
A lightweight React state management library that combines the simplicity of React hooks with the power of event-driven subscriptions. No boilerplate, no complexity—just pure, performant state management.
|
|
10
6
|
|
|
11
7
|
```bash
|
|
12
8
|
npm install react-state-custom
|
|
13
9
|
```
|
|
14
10
|
|
|
15
|
-
##
|
|
11
|
+
## Why React State Custom?
|
|
12
|
+
|
|
13
|
+
**Zero Boilerplate** • **Type-Safe** • **Selective Re-renders** • **Hook-Based** • **~10KB Bundle**
|
|
14
|
+
|
|
15
|
+
React State Custom lets you write state management code that feels natural—because it **is** just React hooks. Use the same hooks you already know (`useState`, `useEffect`, etc.) to create powerful, shared state without learning new paradigms.
|
|
16
|
+
|
|
17
|
+
## ⚡ Quick Example
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { createRootCtx, createAutoCtx, useQuickSubscribe, AutoRootCtx } from 'react-state-custom';
|
|
21
|
+
|
|
22
|
+
// 1. Write your state logic using familiar React hooks
|
|
23
|
+
function useCounterState() {
|
|
24
|
+
const [count, setCount] = useState(0);
|
|
25
|
+
const increment = () => setCount(c => c + 1);
|
|
26
|
+
const decrement = () => setCount(c => c - 1);
|
|
27
|
+
|
|
28
|
+
return { count, increment, decrement };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 2. Create shared context (one line!)
|
|
32
|
+
const { useCtxState } = createAutoCtx(createRootCtx('counter', useCounterState));
|
|
33
|
+
|
|
34
|
+
// 3. Add AutoRootCtx to your app root
|
|
35
|
+
function App() {
|
|
36
|
+
return (
|
|
37
|
+
<>
|
|
38
|
+
<AutoRootCtx />
|
|
39
|
+
<Counter />
|
|
40
|
+
</>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 4. Use anywhere in your app
|
|
45
|
+
function Counter() {
|
|
46
|
+
const ctx = useCtxState();
|
|
47
|
+
const { count, increment, decrement } = useQuickSubscribe(ctx);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div>
|
|
51
|
+
<h1>{count}</h1>
|
|
52
|
+
<button onClick={increment}>+</button>
|
|
53
|
+
<button onClick={decrement}>-</button>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**That's it!** No reducers, no actions, no providers to wrap—just hooks.
|
|
60
|
+
|
|
61
|
+
## 🎯 Key Features
|
|
62
|
+
|
|
63
|
+
### 1. **Just React Hooks**
|
|
64
|
+
Use `useState`, `useEffect`, `useMemo`, and any other React hooks you already know. No new concepts to learn.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
function useUserState({ userId }: { userId: string }) {
|
|
68
|
+
const [user, setUser] = useState(null);
|
|
69
|
+
const [loading, setLoading] = useState(true);
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
fetchUser(userId).then(setUser).finally(() => setLoading(false));
|
|
73
|
+
}, [userId]);
|
|
74
|
+
|
|
75
|
+
return { user, loading };
|
|
76
|
+
}
|
|
77
|
+
```
|
|
16
78
|
|
|
17
|
-
|
|
79
|
+
### 2. **Selective Re-renders**
|
|
80
|
+
Components only re-render when the **specific data they subscribe to** changes—not when anything in the state changes.
|
|
18
81
|
|
|
19
|
-
|
|
82
|
+
```typescript
|
|
83
|
+
// Only re-renders when 'user' changes, not when 'loading' changes
|
|
84
|
+
const { user } = useDataSubscribeMultiple(ctx, 'user');
|
|
20
85
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- **Utility Hooks** - Performance optimization tools
|
|
86
|
+
// Or subscribe to multiple fields
|
|
87
|
+
const { user, loading } = useDataSubscribeMultiple(ctx, 'user', 'loading');
|
|
88
|
+
```
|
|
25
89
|
|
|
26
|
-
###
|
|
90
|
+
### 3. **Automatic Context Management**
|
|
91
|
+
With `AutoRootCtx`, state contexts are automatically created and destroyed as needed. No manual provider management.
|
|
27
92
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- ✅ **Flexibility** - Works with any data structure
|
|
31
|
-
- ✅ **Developer Experience** - Rich debugging and error checking
|
|
32
|
-
- ✅ **Minimal Boilerplate** - Automated context management
|
|
93
|
+
### 4. **TypeScript First**
|
|
94
|
+
Full type inference and type safety throughout. Your IDE knows exactly what's in your state.
|
|
33
95
|
|
|
34
|
-
|
|
96
|
+
### 5. **Tiny Bundle Size**
|
|
97
|
+
~10KB gzipped. No dependencies except React.
|
|
35
98
|
|
|
36
|
-
|
|
99
|
+
## 🆚 Comparison with Redux & Zustand
|
|
37
100
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
101
|
+
| Feature | React State Custom | Redux | Zustand |
|
|
102
|
+
|---------|-------------------|-------|---------|
|
|
103
|
+
| **Bundle Size** | ~10KB | ~50KB (with toolkit) | ~1KB |
|
|
104
|
+
| **Learning Curve** | ✅ Minimal (just hooks) | ❌ High (actions, reducers, middleware) | ✅ Low |
|
|
105
|
+
| **Boilerplate** | ✅ None | ❌ Heavy | ✅ Minimal |
|
|
106
|
+
| **Type Safety** | ✅ Full inference | ⚠️ Requires setup | ✅ Good |
|
|
107
|
+
| **Selective Re-renders** | ✅ Built-in | ⚠️ Requires selectors | ✅ Built-in |
|
|
108
|
+
| **DevTools** | ⚠️ Console logging | ✅ Redux DevTools | ✅ DevTools support |
|
|
109
|
+
| **Async Support** | ✅ Native (hooks) | ⚠️ Requires middleware | ✅ Native |
|
|
110
|
+
| **Context Composition** | ✅ Automatic | ❌ Manual | ⚠️ Manual store combination |
|
|
46
111
|
|
|
47
|
-
|
|
112
|
+
### When to Use React State Custom
|
|
48
113
|
|
|
49
|
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
114
|
+
✅ **Choose React State Custom if you:**
|
|
115
|
+
- Want to use React hooks for state management without learning new patterns
|
|
116
|
+
- Need fine-grained control over component re-renders
|
|
117
|
+
- Prefer minimal boilerplate and configuration
|
|
118
|
+
- Want automatic context lifecycle management
|
|
119
|
+
- Need multiple independent state contexts that don't interfere
|
|
53
120
|
|
|
54
|
-
|
|
121
|
+
❌ **Consider Redux if you:**
|
|
122
|
+
- Need powerful time-travel debugging (Redux DevTools)
|
|
123
|
+
- Have a very large team that benefits from strict architectural patterns
|
|
124
|
+
- Already have significant Redux investment
|
|
55
125
|
|
|
56
|
-
|
|
126
|
+
❌ **Consider Zustand if you:**
|
|
127
|
+
- Want the absolute smallest bundle size
|
|
128
|
+
- Need a simple global store without context isolation
|
|
129
|
+
- Don't need automatic context lifecycle management
|
|
57
130
|
|
|
131
|
+
## 🔥 Real-World Example: User Authentication
|
|
58
132
|
|
|
59
|
-
file main.tsx
|
|
60
133
|
```typescript
|
|
61
|
-
|
|
134
|
+
// authState.ts
|
|
135
|
+
function useAuthState() {
|
|
136
|
+
const [user, setUser] = useState<User | null>(null);
|
|
137
|
+
const [loading, setLoading] = useState(true);
|
|
138
|
+
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
// Check authentication on mount
|
|
141
|
+
checkAuth().then(setUser).finally(() => setLoading(false));
|
|
142
|
+
}, []);
|
|
143
|
+
|
|
144
|
+
const login = async (email: string, password: string) => {
|
|
145
|
+
setLoading(true);
|
|
146
|
+
try {
|
|
147
|
+
const user = await authService.login(email, password);
|
|
148
|
+
setUser(user);
|
|
149
|
+
} finally {
|
|
150
|
+
setLoading(false);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const logout = async () => {
|
|
155
|
+
await authService.logout();
|
|
156
|
+
setUser(null);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return { user, loading, login, logout };
|
|
160
|
+
}
|
|
62
161
|
|
|
63
|
-
|
|
64
|
-
|
|
162
|
+
export const { useCtxState: useAuthState } = createAutoCtx(
|
|
163
|
+
createRootCtx('auth', useAuthState)
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// App.tsx
|
|
167
|
+
function App() {
|
|
65
168
|
return (
|
|
66
169
|
<>
|
|
67
170
|
<AutoRootCtx />
|
|
68
|
-
|
|
171
|
+
<Router>
|
|
172
|
+
<Header />
|
|
173
|
+
<Routes />
|
|
174
|
+
</Router>
|
|
69
175
|
</>
|
|
70
176
|
);
|
|
71
177
|
}
|
|
72
178
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
179
|
+
// Header.tsx - Only re-renders when user changes
|
|
180
|
+
function Header() {
|
|
181
|
+
const ctx = useAuthState();
|
|
182
|
+
const { user, logout } = useQuickSubscribe(ctx);
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<header>
|
|
186
|
+
{user ? (
|
|
187
|
+
<>
|
|
188
|
+
<span>Welcome, {user.name}</span>
|
|
189
|
+
<button onClick={logout}>Logout</button>
|
|
190
|
+
</>
|
|
191
|
+
) : (
|
|
192
|
+
<Link to="/login">Login</Link>
|
|
193
|
+
)}
|
|
194
|
+
</header>
|
|
195
|
+
);
|
|
82
196
|
}
|
|
83
197
|
|
|
84
|
-
//
|
|
85
|
-
function
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const [error, setError] = useState<string | null>(null);
|
|
198
|
+
// ProtectedRoute.tsx - Only re-renders when loading or user changes
|
|
199
|
+
function ProtectedRoute({ children }) {
|
|
200
|
+
const ctx = useAuthState();
|
|
201
|
+
const { user, loading } = useDataSubscribeMultiple(ctx, 'user', 'loading');
|
|
89
202
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}, [props.userId]);
|
|
203
|
+
if (loading) return <Spinner />;
|
|
204
|
+
if (!user) return <Navigate to="/login" />;
|
|
93
205
|
|
|
94
|
-
return
|
|
206
|
+
return children;
|
|
95
207
|
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Compare with Redux:**
|
|
211
|
+
```typescript
|
|
212
|
+
// Redux requires: action types, action creators, reducers, thunks/sagas
|
|
213
|
+
// React State Custom: just write a hook! ✨
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## 📚 Core Concepts
|
|
96
217
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
218
|
+
### 1. **Context** - Event-driven state container
|
|
219
|
+
```typescript
|
|
220
|
+
const ctx = useDataContext<MyState>('my-state');
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### 2. **Data Source** - Publish values to context
|
|
224
|
+
```typescript
|
|
225
|
+
useDataSource(ctx, 'count', count);
|
|
226
|
+
```
|
|
102
227
|
|
|
103
|
-
|
|
228
|
+
### 3. **Data Subscription** - Subscribe to specific values
|
|
229
|
+
```typescript
|
|
230
|
+
const count = useDataSubscribe(ctx, 'count');
|
|
231
|
+
const { count, name } = useDataSubscribeMultiple(ctx, 'count', 'name');
|
|
232
|
+
```
|
|
104
233
|
|
|
234
|
+
### 4. **Root Context** - Lifecycle-managed context
|
|
235
|
+
```typescript
|
|
236
|
+
const { Root, useCtxState } = createRootCtx('my-state', useMyState);
|
|
105
237
|
```
|
|
106
238
|
|
|
107
|
-
|
|
239
|
+
### 5. **Auto Context** - Automatic instance management
|
|
108
240
|
```typescript
|
|
109
|
-
|
|
110
|
-
|
|
241
|
+
const { useCtxState } = createAutoCtx(rootContext);
|
|
242
|
+
```
|
|
111
243
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return <div>Welcome, {user?.name}!</div>;
|
|
244
|
+
## 🚀 Advanced Features
|
|
245
|
+
|
|
246
|
+
### Parameterized Contexts
|
|
247
|
+
Create multiple instances of the same state with different parameters:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
function useUserState({ userId }: { userId: string }) {
|
|
251
|
+
// State logic here
|
|
121
252
|
}
|
|
122
253
|
|
|
123
|
-
|
|
254
|
+
const { useCtxState: useUserState } = createAutoCtx(
|
|
255
|
+
createRootCtx('user', useUserState)
|
|
256
|
+
);
|
|
124
257
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (error) return <div>Error: {error}</div>;
|
|
131
|
-
|
|
132
|
-
return <div>Welcome, {user?.name}!</div>;
|
|
258
|
+
// Different instances for different users
|
|
259
|
+
function UserProfile({ userId }) {
|
|
260
|
+
const ctx = useUserState({ userId }); // Automatic instance per userId
|
|
261
|
+
const { user } = useQuickSubscribe(ctx);
|
|
262
|
+
return <div>{user?.name}</div>;
|
|
133
263
|
}
|
|
134
264
|
```
|
|
135
265
|
|
|
266
|
+
### Debounced Subscriptions
|
|
267
|
+
Optimize performance for frequently changing values:
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
// Re-render at most once per 300ms
|
|
271
|
+
const searchQuery = useDataSubscribe(ctx, 'searchQuery', 300);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Transformed Subscriptions
|
|
275
|
+
Transform data before using it:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
const userStats = useDataSubscribeWithTransform(
|
|
279
|
+
ctx,
|
|
280
|
+
'user',
|
|
281
|
+
(user) => ({
|
|
282
|
+
fullName: `${user?.firstName} ${user?.lastName}`,
|
|
283
|
+
isAdmin: user?.role === 'admin'
|
|
284
|
+
})
|
|
285
|
+
);
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## 📖 Documentation
|
|
289
|
+
|
|
290
|
+
For complete API documentation, examples, and advanced patterns, see:
|
|
291
|
+
- **[API_DOCUMENTATION.md](./API_DOCUMENTATION.md)** - Complete API reference
|
|
292
|
+
|
|
293
|
+
## 🎓 Learning Path
|
|
294
|
+
|
|
295
|
+
1. **Start Simple** - Use `createRootCtx` + `createAutoCtx` for basic state
|
|
296
|
+
2. **Add Subscriptions** - Use `useDataSubscribeMultiple` for selective re-renders
|
|
297
|
+
3. **Optimize** - Add debouncing and transformations as needed
|
|
298
|
+
4. **Scale** - Create parameterized contexts for dynamic instances
|
|
299
|
+
|
|
300
|
+
## 📦 Installation
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
npm install react-state-custom
|
|
304
|
+
# or
|
|
305
|
+
yarn add react-state-custom
|
|
306
|
+
# or
|
|
307
|
+
pnpm add react-state-custom
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## 🤝 Contributing
|
|
311
|
+
|
|
312
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
313
|
+
|
|
136
314
|
## 📄 License
|
|
137
315
|
|
|
138
|
-
MIT License -
|
|
316
|
+
MIT License - feel free to use in any project.
|
|
139
317
|
|
|
140
318
|
---
|
|
141
319
|
|
|
142
|
-
|
|
320
|
+
**Made with ❤️ for developers who love React hooks**
|
package/package.json
CHANGED