state-jet 2.0.16 → 2.0.17
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 +263 -45
- package/package.json +23 -2
package/README.md
CHANGED
|
@@ -1,7 +1,41 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/state-jet)
|
|
2
|
+
[](https://npm-stat.com/charts.html?package=state-jet)
|
|
3
|
+
[](https://github.com/venkateshsundaram/state-jet/issues)
|
|
4
|
+
|
|
1
5
|
A zero-boilerplate, ultra-fast global state management library for React.
|
|
2
6
|
|
|
3
7
|
For more details, see [here](https://statejet.netlify.app).
|
|
4
8
|
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [🚀 Why state-jet?](#🚀-why-state-jet?)
|
|
12
|
+
- [Documentation](#documentation)
|
|
13
|
+
- [🛠 Installation](#🛠-installation)
|
|
14
|
+
- [GlobalState](#globalstate)
|
|
15
|
+
- [Create GlobalState](#create-globalstate)
|
|
16
|
+
- [Binding Global State to a Component](#binding-global-state-to-a-component)
|
|
17
|
+
- [Slices](#slices)
|
|
18
|
+
- [Create Slice](#create-slice)
|
|
19
|
+
- [Store](#store)
|
|
20
|
+
- [Create Store](#create-store)
|
|
21
|
+
- [Binding Global State to a Component](#binding-global-state-to-a-component)
|
|
22
|
+
- [Middlewares](#middlewares)
|
|
23
|
+
- [Logger Middleware](#logger-middleware)
|
|
24
|
+
- [Reducer Middleware](#reducer-middleware)
|
|
25
|
+
- [Debounce Middleware](#debounce-middleware)
|
|
26
|
+
- [Optimistic Middleware](#optimistic-middleware)
|
|
27
|
+
- [Custom Middleware](#custom-middleware)
|
|
28
|
+
- [Typescript Usage](#typescript-usage)
|
|
29
|
+
- [Why state-jet Is More Advanced Than Zustand](#why-state-jet-is-more-advanced-than-zustand)
|
|
30
|
+
- [FAQ](#faq)
|
|
31
|
+
- [Conclusion](#conclusion)
|
|
32
|
+
- [⚡ Comparison Table](#⚡-comparison-table)
|
|
33
|
+
- [Comparison with other libraries](#comparison-with-other-libraries)
|
|
34
|
+
- [Contributing](#contributing)
|
|
35
|
+
- [Publishing](#publishing)
|
|
36
|
+
- [Feedbacks and Issues](#feedbacks-and-issues)
|
|
37
|
+
- [License](#license)
|
|
38
|
+
|
|
5
39
|
## 🚀 Why state-jet?
|
|
6
40
|
|
|
7
41
|
- ✅ **No Context, No Providers** – Works outside React, reducing unnecessary re-renders.
|
|
@@ -47,14 +81,31 @@ The `useStateGlobal` hook is the simplest entry point to State-Jet. It allows yo
|
|
|
47
81
|
|
|
48
82
|
### Create GlobalState
|
|
49
83
|
|
|
50
|
-
```
|
|
84
|
+
```ts
|
|
85
|
+
// file: src/store/index.ts
|
|
86
|
+
|
|
51
87
|
import { useStateGlobal } from "state-jet";
|
|
52
88
|
|
|
53
|
-
const
|
|
89
|
+
export const counterState = useStateGlobal("counter", 0);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Binding Global State to a Component
|
|
54
93
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
94
|
+
```tsx
|
|
95
|
+
// file: src/components/Counter.tsx
|
|
96
|
+
|
|
97
|
+
import { counterState } from "../store";
|
|
98
|
+
|
|
99
|
+
export default function Counter() {
|
|
100
|
+
const count = counterState.useState() as number;
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div>
|
|
104
|
+
<h1>Counter: {count}</h1>
|
|
105
|
+
<button onClick={() => counterState.set(count - 1)}>Decrement</button>
|
|
106
|
+
<button onClick={() => counterState.set(count + 1)}>Increment</button>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
58
109
|
}
|
|
59
110
|
```
|
|
60
111
|
|
|
@@ -66,7 +117,9 @@ Each slice can contain multiple state values, each identified by a unique key wi
|
|
|
66
117
|
|
|
67
118
|
### Create Slice
|
|
68
119
|
|
|
69
|
-
```
|
|
120
|
+
```ts
|
|
121
|
+
// file: src/store/slices.ts
|
|
122
|
+
|
|
70
123
|
import { useSlice } from "state-jet";
|
|
71
124
|
|
|
72
125
|
const productSlice = useSlice("products");
|
|
@@ -82,16 +135,76 @@ The `useStore` hook serves as a mechanism to group related slices of state into
|
|
|
82
135
|
|
|
83
136
|
### Create Store
|
|
84
137
|
|
|
85
|
-
```
|
|
138
|
+
```ts
|
|
139
|
+
// file: src/store/index.ts
|
|
140
|
+
|
|
86
141
|
import { useStore } from "state-jet";
|
|
87
142
|
import { useProductSlice, useCartSlice } from "./slices";
|
|
88
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Ecommerce store with product and cart slices
|
|
146
|
+
*/
|
|
89
147
|
const initializer: any = () => ({
|
|
90
148
|
products: useProductSlice(),
|
|
91
149
|
cart: useCartSlice(),
|
|
92
150
|
});
|
|
93
151
|
|
|
94
|
-
export const
|
|
152
|
+
export const useEcommerceStore = () => useStore(initializer);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Binding Global State to a Component
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
// file: src/components/ProductList.tsx
|
|
159
|
+
|
|
160
|
+
import { useEcommerceStore } from "../store";
|
|
161
|
+
|
|
162
|
+
type ProductType = {
|
|
163
|
+
name: string,
|
|
164
|
+
price: number
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
type CartType = {
|
|
168
|
+
name: string,
|
|
169
|
+
price: number,
|
|
170
|
+
count: number
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const ProductList = () => {
|
|
174
|
+
const store = useEcommerceStore();
|
|
175
|
+
const products: any = store.products;
|
|
176
|
+
const cart: any = store.cart;
|
|
177
|
+
const productItems = products.useState() as ProductType[];
|
|
178
|
+
const cartItems = cart.useState() as CartType[];
|
|
179
|
+
|
|
180
|
+
const addToCart = (product: ProductType) => {
|
|
181
|
+
if (cartItems.some((cartItem: CartType) => cartItem.name === product.name)) {
|
|
182
|
+
cart.set(cartItems.map((cartItem: CartType) => {
|
|
183
|
+
if (cartItem.name === product.name) {
|
|
184
|
+
return { ...cartItem, count: (cartItem.count || 0) + 1 };
|
|
185
|
+
}
|
|
186
|
+
return cartItem;
|
|
187
|
+
}));
|
|
188
|
+
} else {
|
|
189
|
+
cart.set([...cartItems, { ...product, count: 1 }]);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
<div>
|
|
195
|
+
<h2>🛍️ Products</h2>
|
|
196
|
+
<ul>
|
|
197
|
+
{productItems.map((productItem: ProductType, index: number) => (
|
|
198
|
+
<li key={index}>
|
|
199
|
+
{productItem.name} - ${productItem.price}{" "}
|
|
200
|
+
<button onClick={() => addToCart(productItem)}>Add to Cart</button>
|
|
201
|
+
</li>
|
|
202
|
+
))}
|
|
203
|
+
</ul>
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
};
|
|
207
|
+
|
|
95
208
|
```
|
|
96
209
|
|
|
97
210
|
## Middlewares
|
|
@@ -111,23 +224,42 @@ function useStateGlobal<T>(
|
|
|
111
224
|
|
|
112
225
|
You can log your store for every action.
|
|
113
226
|
|
|
114
|
-
```
|
|
115
|
-
|
|
227
|
+
```ts
|
|
228
|
+
// file: src/store/middleware.ts
|
|
116
229
|
|
|
117
|
-
const loggerMiddleware = (key: string, prev: number, next: number) => {
|
|
230
|
+
export const loggerMiddleware = (key: string, prev: number, next: number) => {
|
|
118
231
|
console.log(`[state-jet] ${key}: ${prev} → ${next}`);
|
|
119
232
|
};
|
|
120
233
|
|
|
121
|
-
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Create global state with loggerMiddleware
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
// file: src/store/index.ts
|
|
240
|
+
|
|
241
|
+
import { useStateGlobal } from "state-jet";
|
|
242
|
+
import { loggerMiddleware } from "./middleware";
|
|
243
|
+
|
|
244
|
+
export const counterState = useStateGlobal("counter", 0, { middleware: [loggerMiddleware] });
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Binding Global State to a Component
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
// file: src/components/Counter.tsx
|
|
252
|
+
|
|
253
|
+
import { counterState } from "../store";
|
|
122
254
|
|
|
123
255
|
export default function Counter() {
|
|
124
|
-
const count =
|
|
256
|
+
const count = counterState.useState() as number;
|
|
125
257
|
|
|
126
258
|
return (
|
|
127
259
|
<div>
|
|
128
260
|
<h1>Counter: {count}</h1>
|
|
129
|
-
<button onClick={() =>
|
|
130
|
-
<button onClick={() =>
|
|
261
|
+
<button onClick={() => counterState.set(count - 1)}>Decrement</button>
|
|
262
|
+
<button onClick={() => counterState.set(count + 1)}>Increment</button>
|
|
131
263
|
</div>
|
|
132
264
|
);
|
|
133
265
|
}
|
|
@@ -137,8 +269,8 @@ export default function Counter() {
|
|
|
137
269
|
|
|
138
270
|
Can't live without reducer?. No worries. StateJet supports reducer middleware
|
|
139
271
|
|
|
140
|
-
```
|
|
141
|
-
|
|
272
|
+
```ts
|
|
273
|
+
// file: src/store/middleware.ts
|
|
142
274
|
|
|
143
275
|
type Action<T> = { type: string; payload?: T };
|
|
144
276
|
type Middleware<T> = (
|
|
@@ -148,7 +280,7 @@ type Middleware<T> = (
|
|
|
148
280
|
set?: (value: T) => void,
|
|
149
281
|
) => T | void | Promise<void>;
|
|
150
282
|
|
|
151
|
-
const reducerMiddleware: Middleware<number> = (key, prev, action: Action<any>) => {
|
|
283
|
+
export const reducerMiddleware: Middleware<number> = (key, prev, action: Action<any>) => {
|
|
152
284
|
switch (action.type) {
|
|
153
285
|
case "INCREMENT":
|
|
154
286
|
return prev + 1;
|
|
@@ -160,31 +292,83 @@ const reducerMiddleware: Middleware<number> = (key, prev, action: Action<any>) =
|
|
|
160
292
|
return prev;
|
|
161
293
|
}
|
|
162
294
|
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Create global state with reducerMiddleware
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
// file: src/store/index.ts
|
|
301
|
+
|
|
302
|
+
import { useStateGlobal } from "state-jet";
|
|
303
|
+
import { reducerMiddleware } from "./middleware";
|
|
304
|
+
|
|
305
|
+
export const counterState = useStateGlobal("counter", 0, { middleware: [reducerMiddleware] });
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Binding Global State to a Component
|
|
163
310
|
|
|
164
|
-
|
|
311
|
+
```tsx
|
|
312
|
+
// file: src/components/Counter.tsx
|
|
313
|
+
|
|
314
|
+
import { counterState } from "../store";
|
|
165
315
|
|
|
166
316
|
export default function Counter() {
|
|
167
|
-
const count =
|
|
317
|
+
const count = counterState.useState() as number;
|
|
168
318
|
|
|
169
319
|
return (
|
|
170
320
|
<div>
|
|
171
321
|
<h1>Counter: {count}</h1>
|
|
172
|
-
<button onClick={() =>
|
|
173
|
-
<button onClick={() =>
|
|
174
|
-
<button onClick={() =>
|
|
322
|
+
<button onClick={() => counterState.set({ type: "DECREMENT" })}>Decrement</button>
|
|
323
|
+
<button onClick={() => counterState.set({ type: "INCREMENT" })}>Increment</button>
|
|
324
|
+
<button onClick={() => counterState.set({ type: "RESET" })}>Reset</button>
|
|
175
325
|
</div>
|
|
176
326
|
);
|
|
177
327
|
}
|
|
178
328
|
```
|
|
179
329
|
|
|
330
|
+
### Debounce Middleware
|
|
331
|
+
|
|
332
|
+
You can delay the update of global state
|
|
333
|
+
|
|
334
|
+
```ts
|
|
335
|
+
// file: src/store/middleware.ts
|
|
336
|
+
|
|
337
|
+
let timer: ReturnType<typeof setTimeout>;
|
|
338
|
+
|
|
339
|
+
// Debounce middleware with delay
|
|
340
|
+
export const debounceMiddleware = (delay: number) => {
|
|
341
|
+
return (key: string, prev: number, next: any, set?: (value: any) => void) => {
|
|
342
|
+
clearTimeout(timer);
|
|
343
|
+
if (set) {
|
|
344
|
+
timer = setTimeout(() => {
|
|
345
|
+
console.log(`[state-jet] Debounced: ${key} → ${next}`);
|
|
346
|
+
set(next); // Apply the debounced update
|
|
347
|
+
}, delay);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
};
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Create global state with debounceMiddleware
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
// file: src/store/index.ts
|
|
357
|
+
|
|
358
|
+
import { useStateGlobal } from "state-jet";
|
|
359
|
+
import { debounceMiddleware } from "./middleware";
|
|
360
|
+
|
|
361
|
+
export const counterState = useStateGlobal("counter", 0, { middleware: [debounceMiddleware(500)] });
|
|
362
|
+
```
|
|
363
|
+
|
|
180
364
|
### Optimistic Middleware
|
|
181
365
|
|
|
182
366
|
You can optimistically update global state with rollback support
|
|
183
367
|
|
|
184
|
-
```
|
|
185
|
-
|
|
368
|
+
```ts
|
|
369
|
+
// file: src/store/middleware.ts
|
|
186
370
|
|
|
187
|
-
const optimisticMiddleware = (apiUrl: string) => {
|
|
371
|
+
export const optimisticMiddleware = (apiUrl: string) => {
|
|
188
372
|
return async (key: string, prev: number, next: number, set: any) => {
|
|
189
373
|
set(next); // Optimistically update state
|
|
190
374
|
|
|
@@ -200,26 +384,55 @@ const optimisticMiddleware = (apiUrl: string) => {
|
|
|
200
384
|
}
|
|
201
385
|
};
|
|
202
386
|
};
|
|
203
|
-
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Create global state with optimisticMiddleware
|
|
390
|
+
|
|
391
|
+
```ts
|
|
392
|
+
// file: src/store/index.ts
|
|
393
|
+
|
|
394
|
+
import { useStateGlobal } from "state-jet";
|
|
395
|
+
import { optimisticMiddleware } from "./middleware";
|
|
396
|
+
|
|
397
|
+
export const profileState = useStateGlobal("profile", { name: "John" }, {
|
|
204
398
|
middleware: [optimisticMiddleware("/update-profile")],
|
|
205
399
|
});
|
|
206
400
|
```
|
|
207
401
|
|
|
208
402
|
### Custom Middleware
|
|
209
403
|
|
|
210
|
-
You can create your own custom middleware in state-jet
|
|
404
|
+
You can also create your own custom middleware in state-jet
|
|
211
405
|
|
|
212
|
-
```
|
|
213
|
-
|
|
406
|
+
```ts
|
|
407
|
+
// file: src/store/middleware.ts
|
|
214
408
|
|
|
215
|
-
const validateAgeMiddleware = (key: string, prev: number, next: number) => {
|
|
409
|
+
export const validateAgeMiddleware = (key: string, prev: number, next: number) => {
|
|
216
410
|
if (next < 0) {
|
|
217
411
|
console.warn("Age cannot be negative!");
|
|
218
412
|
return prev;
|
|
219
413
|
}
|
|
220
414
|
return next;
|
|
221
415
|
};
|
|
222
|
-
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Create global state with validateAgeMiddleware
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
// file: src/store/index.ts
|
|
422
|
+
|
|
423
|
+
import { useStateGlobal } from "state-jet";
|
|
424
|
+
import { validateAgeMiddleware } from "./middleware";
|
|
425
|
+
|
|
426
|
+
export const ageState = useStateGlobal("age", 0, { middleware: [validateAgeMiddleware] });
|
|
427
|
+
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Binding Global State to a Component
|
|
431
|
+
|
|
432
|
+
```tsx
|
|
433
|
+
// file: src/components/Profile.tsx
|
|
434
|
+
|
|
435
|
+
import { ageState } from "../store";
|
|
223
436
|
|
|
224
437
|
export default function Profile() {
|
|
225
438
|
const age = ageState.useState() as number;
|
|
@@ -229,7 +442,7 @@ export default function Profile() {
|
|
|
229
442
|
<h1>Age: {age}</h1>
|
|
230
443
|
<button
|
|
231
444
|
onClick={() => {
|
|
232
|
-
|
|
445
|
+
ageState.set(-5) // Age will be 0 eventhough it updated with negative value due to middleware logic
|
|
233
446
|
}}>
|
|
234
447
|
Set negative
|
|
235
448
|
</button>
|
|
@@ -256,19 +469,18 @@ const todoState = useStateGlobal<Todo[]>("todos", []);
|
|
|
256
469
|
|
|
257
470
|
## Why state-jet Is More Advanced Than Zustand
|
|
258
471
|
|
|
259
|
-
- **No Proxies Needed
|
|
260
|
-
|
|
261
|
-
- **
|
|
262
|
-
|
|
263
|
-
- **
|
|
264
|
-
→ Unlike Zustand, state-jet has built-in support for instant UI updates and auto-revert on failures.
|
|
265
|
-
- **Multi-Tab Sync**
|
|
266
|
-
→ global state persists across browser tabs and devices.
|
|
267
|
-
- **CRDT Support**
|
|
268
|
-
→ Automatic conflict resolution for real-time apps, something even Zustand lacks.
|
|
472
|
+
- **No Proxies Needed**: Zustand uses proxies for state updates, but state-jet uses signals, making it even faster.
|
|
473
|
+
- **Derived State Is Automatic**: No need for selectors; state updates only trigger where necessary.
|
|
474
|
+
- **Optimistic Updates & Rollback**: Unlike Zustand, state-jet has built-in support for instant UI updates and auto-revert on failures.
|
|
475
|
+
- **Multi-Tab Sync**: Global state persists across browser tabs and devices.
|
|
476
|
+
- **CRDT Support**: Automatic conflict resolution for real-time apps, something even Zustand lacks.
|
|
269
477
|
|
|
478
|
+
## FAQ
|
|
479
|
+
- If you want to manage your global state like `useState` as usual.
|
|
480
|
+
- If you want to manage your global state without involving in setting up Provider Component, Dispatcher, Reducer, etc.
|
|
481
|
+
- If you want to see `Redux` or `Context API` alternative.
|
|
270
482
|
|
|
271
|
-
|
|
483
|
+
## Conclusion
|
|
272
484
|
|
|
273
485
|
If you need the simplest, fastest, and most advanced state management solution for React, state-jet beats Redux, Recoil, MobX, Jotai, and even Zustand in performance, reactivity, and developer experience. 🚀
|
|
274
486
|
|
|
@@ -295,6 +507,12 @@ Development of State-jet happens in the open on GitHub, and we are grateful to t
|
|
|
295
507
|
|
|
296
508
|
- [Contributing Guide](./CONTRIBUTING.md)
|
|
297
509
|
|
|
298
|
-
|
|
510
|
+
## Publishing
|
|
511
|
+
- Before pushing your changes to Github, make sure that `version` in `package.json` is changed to newest version. Then run `npm install` for synchronize it to `package-lock.json` and `pnpm install` for synchronize it to `pnpm-lock.yaml`
|
|
512
|
+
|
|
513
|
+
## Feedbacks and Issues
|
|
514
|
+
Feel free to open issues if you found any feedback or issues on `state-jet`. And feel free if you want to contribute too! 😄
|
|
515
|
+
|
|
516
|
+
## License
|
|
299
517
|
|
|
300
518
|
State-jet is [MIT licensed](./LICENSE).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "state-jet",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.17",
|
|
4
4
|
"description": "Ultra-lightweight global state management for React",
|
|
5
5
|
"main": "dist/index.cjs",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -31,8 +31,29 @@
|
|
|
31
31
|
"global-state-management",
|
|
32
32
|
"signals",
|
|
33
33
|
"state-jet",
|
|
34
|
+
"statejet",
|
|
35
|
+
"state-jet-react",
|
|
34
36
|
"jet",
|
|
35
|
-
"
|
|
37
|
+
"react-jet",
|
|
38
|
+
"jetstate",
|
|
39
|
+
"jet-state",
|
|
40
|
+
"react-jetstate",
|
|
41
|
+
"react-hooks",
|
|
42
|
+
"store",
|
|
43
|
+
"hooks",
|
|
44
|
+
"javascript",
|
|
45
|
+
"typescript",
|
|
46
|
+
"global",
|
|
47
|
+
"react-state-management",
|
|
48
|
+
"state-manager",
|
|
49
|
+
"global-state-hooks",
|
|
50
|
+
"state-hooks",
|
|
51
|
+
"use-store",
|
|
52
|
+
"client-state",
|
|
53
|
+
"react-utility",
|
|
54
|
+
"hooks-utility",
|
|
55
|
+
"redux-alternative",
|
|
56
|
+
"context-alternative"
|
|
36
57
|
],
|
|
37
58
|
"author": "venkateshsundaram",
|
|
38
59
|
"license": "MIT",
|