react-state-custom 1.0.16 → 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 +257 -134
- package/dist/index.es.js +140 -133
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/state-utils/createAutoCtx.d.ts +1 -1
- package/package.json +9 -2
- package/src/state-utils/createAutoCtx.tsx +10 -2
package/README.md
CHANGED
|
@@ -1,197 +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?
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
**Zero Boilerplate** • **Type-Safe** • **Selective Re-renders** • **Hook-Based** • **~10KB Bundle**
|
|
18
14
|
|
|
19
|
-
|
|
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.
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
- **Root Context Factory** - Automated context lifecycle management
|
|
23
|
-
- **Auto Context System** - Self-managing context instances
|
|
24
|
-
- **Utility Hooks** - Performance optimization tools
|
|
17
|
+
## ⚡ Quick Example
|
|
25
18
|
|
|
26
|
-
|
|
19
|
+
```typescript
|
|
20
|
+
import { createRootCtx, createAutoCtx, useQuickSubscribe, AutoRootCtx } from 'react-state-custom';
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
+
}
|
|
33
30
|
|
|
34
|
-
|
|
31
|
+
// 2. Create shared context (one line!)
|
|
32
|
+
const { useCtxState } = createAutoCtx(createRootCtx('counter', useCounterState));
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
// 3. Add AutoRootCtx to your app root
|
|
35
|
+
function App() {
|
|
36
|
+
return (
|
|
37
|
+
<>
|
|
38
|
+
<AutoRootCtx />
|
|
39
|
+
<Counter />
|
|
40
|
+
</>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
37
43
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
+
```
|
|
46
58
|
|
|
47
|
-
|
|
59
|
+
**That's it!** No reducers, no actions, no providers to wrap—just hooks.
|
|
48
60
|
|
|
49
|
-
|
|
50
|
-
- **Component Communication** - Share data between distant components
|
|
51
|
-
- **Performance Optimization** - Minimize unnecessary re-renders
|
|
52
|
-
- **Context Composition** - Combine multiple contexts efficiently
|
|
61
|
+
## 🎯 Key Features
|
|
53
62
|
|
|
54
|
-
|
|
63
|
+
### 1. **Just React Hooks**
|
|
64
|
+
Use `useState`, `useEffect`, `useMemo`, and any other React hooks you already know. No new concepts to learn.
|
|
55
65
|
|
|
56
66
|
```typescript
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
user: User | null;
|
|
61
|
-
theme: 'light' | 'dark';
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Provider component
|
|
65
|
-
function AppProvider({ children }) {
|
|
66
|
-
const ctx = useDataContext<AppState>('app-state');
|
|
67
|
-
const user = useCurrentUser();
|
|
68
|
-
const theme = useTheme();
|
|
67
|
+
function useUserState({ userId }: { userId: string }) {
|
|
68
|
+
const [user, setUser] = useState(null);
|
|
69
|
+
const [loading, setLoading] = useState(true);
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
fetchUser(userId).then(setUser).finally(() => setLoading(false));
|
|
73
|
+
}, [userId]);
|
|
72
74
|
|
|
73
|
-
return
|
|
75
|
+
return { user, loading };
|
|
74
76
|
}
|
|
77
|
+
```
|
|
75
78
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// Only re-renders when 'user' changes, not when 'loading' changes
|
|
84
|
+
const { user } = useDataSubscribeMultiple(ctx, 'user');
|
|
85
|
+
|
|
86
|
+
// Or subscribe to multiple fields
|
|
87
|
+
const { user, loading } = useDataSubscribeMultiple(ctx, 'user', 'loading');
|
|
83
88
|
```
|
|
84
89
|
|
|
85
|
-
|
|
90
|
+
### 3. **Automatic Context Management**
|
|
91
|
+
With `AutoRootCtx`, state contexts are automatically created and destroyed as needed. No manual provider management.
|
|
86
92
|
|
|
87
|
-
###
|
|
93
|
+
### 4. **TypeScript First**
|
|
94
|
+
Full type inference and type safety throughout. Your IDE knows exactly what's in your state.
|
|
88
95
|
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
### 5. **Tiny Bundle Size**
|
|
97
|
+
~10KB gzipped. No dependencies except React.
|
|
91
98
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
## 🆚 Comparison with Redux & Zustand
|
|
100
|
+
|
|
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 |
|
|
111
|
+
|
|
112
|
+
### When to Use React State Custom
|
|
113
|
+
|
|
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
|
|
120
|
+
|
|
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
|
|
125
|
+
|
|
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
|
|
97
130
|
|
|
98
|
-
|
|
99
|
-
|
|
131
|
+
## 🔥 Real-World Example: User Authentication
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
// authState.ts
|
|
135
|
+
function useAuthState() {
|
|
100
136
|
const [user, setUser] = useState<User | null>(null);
|
|
101
137
|
const [loading, setLoading] = useState(true);
|
|
102
|
-
const [error, setError] = useState<string | null>(null);
|
|
103
138
|
|
|
104
139
|
useEffect(() => {
|
|
105
|
-
|
|
106
|
-
|
|
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
|
+
};
|
|
107
153
|
|
|
108
|
-
|
|
154
|
+
const logout = async () => {
|
|
155
|
+
await authService.logout();
|
|
156
|
+
setUser(null);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return { user, loading, login, logout };
|
|
109
160
|
}
|
|
110
161
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
'user-state',
|
|
114
|
-
useUserState
|
|
162
|
+
export const { useCtxState: useAuthState } = createAutoCtx(
|
|
163
|
+
createRootCtx('auth', useAuthState)
|
|
115
164
|
);
|
|
116
165
|
|
|
117
|
-
//
|
|
118
|
-
function
|
|
166
|
+
// App.tsx
|
|
167
|
+
function App() {
|
|
119
168
|
return (
|
|
120
169
|
<>
|
|
121
|
-
<
|
|
122
|
-
|
|
170
|
+
<AutoRootCtx />
|
|
171
|
+
<Router>
|
|
172
|
+
<Header />
|
|
173
|
+
<Routes />
|
|
174
|
+
</Router>
|
|
123
175
|
</>
|
|
124
176
|
);
|
|
125
177
|
}
|
|
126
178
|
|
|
127
|
-
//
|
|
128
|
-
function
|
|
129
|
-
const ctx =
|
|
130
|
-
const { user,
|
|
179
|
+
// Header.tsx - Only re-renders when user changes
|
|
180
|
+
function Header() {
|
|
181
|
+
const ctx = useAuthState();
|
|
182
|
+
const { user, logout } = useQuickSubscribe(ctx);
|
|
131
183
|
|
|
132
|
-
|
|
133
|
-
|
|
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
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
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');
|
|
202
|
+
|
|
203
|
+
if (loading) return <Spinner />;
|
|
204
|
+
if (!user) return <Navigate to="/login" />;
|
|
134
205
|
|
|
135
|
-
return
|
|
206
|
+
return children;
|
|
136
207
|
}
|
|
137
208
|
```
|
|
138
209
|
|
|
139
|
-
|
|
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
|
+
```
|
|
140
215
|
|
|
216
|
+
## 📚 Core Concepts
|
|
217
|
+
|
|
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
|
+
```
|
|
227
|
+
|
|
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
|
+
```
|
|
233
|
+
|
|
234
|
+
### 4. **Root Context** - Lifecycle-managed context
|
|
235
|
+
```typescript
|
|
236
|
+
const { Root, useCtxState } = createRootCtx('my-state', useMyState);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 5. **Auto Context** - Automatic instance management
|
|
141
240
|
```typescript
|
|
142
|
-
|
|
241
|
+
const { useCtxState } = createAutoCtx(rootContext);
|
|
242
|
+
```
|
|
143
243
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
|
149
252
|
}
|
|
150
253
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
<h2>Settings</h2>
|
|
161
|
-
|
|
162
|
-
<label>
|
|
163
|
-
Theme:
|
|
164
|
-
<select value={theme} onChange={(e) => updateSetting('theme', e.target.value)}>
|
|
165
|
-
<option value="light">Light</option>
|
|
166
|
-
<option value="dark">Dark</option>
|
|
167
|
-
</select>
|
|
168
|
-
</label>
|
|
169
|
-
|
|
170
|
-
<label>
|
|
171
|
-
Language:
|
|
172
|
-
<input
|
|
173
|
-
value={language}
|
|
174
|
-
onChange={(e) => updateSetting('language', e.target.value)}
|
|
175
|
-
/>
|
|
176
|
-
</label>
|
|
177
|
-
|
|
178
|
-
<label>
|
|
179
|
-
<input
|
|
180
|
-
type="checkbox"
|
|
181
|
-
checked={notifications}
|
|
182
|
-
onChange={(e) => updateSetting('notifications', e.target.checked)}
|
|
183
|
-
/>
|
|
184
|
-
Enable notifications
|
|
185
|
-
</label>
|
|
186
|
-
</div>
|
|
187
|
-
);
|
|
254
|
+
const { useCtxState: useUserState } = createAutoCtx(
|
|
255
|
+
createRootCtx('user', useUserState)
|
|
256
|
+
);
|
|
257
|
+
|
|
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>;
|
|
188
263
|
}
|
|
189
264
|
```
|
|
190
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
|
+
|
|
191
314
|
## 📄 License
|
|
192
315
|
|
|
193
|
-
MIT License -
|
|
316
|
+
MIT License - feel free to use in any project.
|
|
194
317
|
|
|
195
318
|
---
|
|
196
319
|
|
|
197
|
-
|
|
320
|
+
**Made with ❤️ for developers who love React hooks**
|