reactish-state 1.0.0-alpha.1 → 1.0.0-alpha.3
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
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
> Simple, decentralized (atomic) state management for React.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/reactish-state) [](https://bundlephobia.com/package/reactish-state)
|
|
5
|
+
[](https://www.npmjs.com/package/reactish-state) [](https://bundlephobia.com/package/reactish-state) [](https://bundlejs.com/?q=reactish-state&treeshake=%5B*%5D&config=%7B%22esbuild%22%3A%7B%22external%22%3A%5B%22react%22%5D%7D%7D)
|
|
6
|
+
|
|
7
|
+
💡 [Quick examples](#examples) 🔧 [TypeScript usage](#typescript-usage)
|
|
6
8
|
|
|
7
9
|
## ✨Highlights✨
|
|
8
10
|
|
|
@@ -43,7 +45,7 @@ countState.set(10);
|
|
|
43
45
|
console.log(countState.get()); // Print 10
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
### A state can also have actions bound to it
|
|
48
|
+
### A state can also have custom actions bound to it
|
|
47
49
|
|
|
48
50
|
```js
|
|
49
51
|
const countState = state(0, (set, get) => ({
|
|
@@ -55,8 +57,8 @@ const countState = state(0, (set, get) => ({
|
|
|
55
57
|
decrease: () => set(get() - 1)
|
|
56
58
|
}));
|
|
57
59
|
|
|
58
|
-
// Use the actions
|
|
59
|
-
countState.
|
|
60
|
+
// Use the custom actions
|
|
61
|
+
countState.increase();
|
|
60
62
|
```
|
|
61
63
|
|
|
62
64
|
### `selector` can create derived state
|
|
@@ -90,9 +92,10 @@ const Example = () => {
|
|
|
90
92
|
|
|
91
93
|
return (
|
|
92
94
|
<h1>
|
|
95
|
+
{/* The return values of `useSnapshot` are used for rendering */}
|
|
93
96
|
{count} {triple}
|
|
94
|
-
{/* Update the state using the actions bound to it */}
|
|
95
|
-
<button onClick={() => countState.
|
|
97
|
+
{/* Update the state using the custom actions bound to it */}
|
|
98
|
+
<button onClick={() => countState.increase()}>Increase</button>
|
|
96
99
|
{/* Or update the state using the `set` method directly */}
|
|
97
100
|
<button onClick={() => countState.set((i) => i - 1)}>Decrease</button>
|
|
98
101
|
<button onClick={() => countState.set(0)}>Reset</button>
|
|
@@ -232,11 +235,14 @@ You can also create async actions bound to a state:
|
|
|
232
235
|
|
|
233
236
|
```js
|
|
234
237
|
const todosState = state([], (set) => ({
|
|
235
|
-
|
|
236
|
-
const response = await fetch(url);
|
|
238
|
+
fetchData: async () => {
|
|
239
|
+
const response = await fetch(/* some url */);
|
|
237
240
|
set(await response.json());
|
|
238
241
|
}
|
|
239
242
|
}));
|
|
243
|
+
|
|
244
|
+
// Use the async action
|
|
245
|
+
await todosState.fetchData();
|
|
240
246
|
```
|
|
241
247
|
|
|
242
248
|
## Accessing other state or selectors inside actions
|
|
@@ -293,7 +299,7 @@ const countState = state(0, (set) => ({
|
|
|
293
299
|
increase: () => set((count) => count + 1),
|
|
294
300
|
reset: () => set(0)
|
|
295
301
|
}));
|
|
296
|
-
const { increase, reset } = countState
|
|
302
|
+
const { increase, reset } = countState;
|
|
297
303
|
|
|
298
304
|
const Example = () => {
|
|
299
305
|
const count = useSnapshot(countState);
|
|
@@ -372,7 +378,7 @@ const countState = state(0, (set) => ({
|
|
|
372
378
|
dispatch: (action) => set((state) => reducer(state, action), action)
|
|
373
379
|
}));
|
|
374
380
|
|
|
375
|
-
const { dispatch } = countState
|
|
381
|
+
const { dispatch } = countState;
|
|
376
382
|
dispatch({ type: "INCREASE", by: 10 });
|
|
377
383
|
dispatch({ type: "DECREASE", by: 7 });
|
|
378
384
|
console.log(countState.get()); // Print 3
|
|
@@ -401,7 +407,7 @@ const countState = state(0, (set) => ({
|
|
|
401
407
|
}));
|
|
402
408
|
|
|
403
409
|
countState.set(99); // Print "New state 99"
|
|
404
|
-
countState.
|
|
410
|
+
countState.increase(); // Print "New state 100"
|
|
405
411
|
|
|
406
412
|
// The same `state` function can be reused,
|
|
407
413
|
// so you don't need to set up the middleware again
|
|
@@ -473,8 +479,8 @@ const todos = state([], (set) => ({
|
|
|
473
479
|
}));
|
|
474
480
|
|
|
475
481
|
// Use the actions
|
|
476
|
-
todos.
|
|
477
|
-
todos.
|
|
482
|
+
todos.add("Shop groceries");
|
|
483
|
+
todos.toggle(1);
|
|
478
484
|
```
|
|
479
485
|
|
|
480
486
|
## Redux devtools middleware
|
|
@@ -588,6 +594,68 @@ const selector = createSelector({ plugin: reduxDevtools() });
|
|
|
588
594
|
// Then use the `selector` as usual...
|
|
589
595
|
```
|
|
590
596
|
|
|
597
|
+
# TypeScript usage
|
|
598
|
+
|
|
599
|
+
The API relies on type inference to correctly infer the types for both the value and actions of the state. There are two scenarios:
|
|
600
|
+
|
|
601
|
+
## I. The type of state can be inferred from its initial value
|
|
602
|
+
|
|
603
|
+
In this case, the usage in TypeScript should be identical to JavaScript. You don't need to make any specific effort regarding typing. This is true when the state holds simple or primitive values.
|
|
604
|
+
|
|
605
|
+
```ts
|
|
606
|
+
const countState = state(0, (set) => ({
|
|
607
|
+
increase: (by: number) =>
|
|
608
|
+
set(
|
|
609
|
+
(count) => count + by
|
|
610
|
+
// The `count` is inferred as a number type from the initial value.
|
|
611
|
+
)
|
|
612
|
+
}));
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
## II. The type of state cannot be inferred from its initial value
|
|
616
|
+
|
|
617
|
+
In this case, you have three options:
|
|
618
|
+
|
|
619
|
+
### 1. Use a type assertion to specify a more specific type for the initial value:
|
|
620
|
+
|
|
621
|
+
```ts
|
|
622
|
+
const myTodos = state([] as string[], (set) => ({
|
|
623
|
+
add: (newTodo: string) => set((todos) => [...todos, newTodo])
|
|
624
|
+
}));
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
This is the simplest approach since the types for custom actions will be automatically inferred.
|
|
628
|
+
|
|
629
|
+
### 2. Declare the initial value separately with a specific type:
|
|
630
|
+
|
|
631
|
+
```ts
|
|
632
|
+
const initialValue: string[] = [];
|
|
633
|
+
const myTodos = state(initialValue, (set) => ({
|
|
634
|
+
add: (newTodo: string) => set((todos) => [...todos, newTodo])
|
|
635
|
+
}));
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
This is basically very similar to the first method, except you need to write an additional line of code. The types for actions will be automatically inferred.
|
|
639
|
+
|
|
640
|
+
### 3. Specify type parameters explicitly:
|
|
641
|
+
|
|
642
|
+
```ts
|
|
643
|
+
const myTodos = state<string[], { add: (newTodo: string) => void }>(
|
|
644
|
+
[],
|
|
645
|
+
(set) => ({
|
|
646
|
+
add: (newTodo) => set((todos) => [...todos, newTodo])
|
|
647
|
+
})
|
|
648
|
+
);
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
However, if you choose this method, you need to specify the types for both the state value and actions.
|
|
652
|
+
|
|
653
|
+
# Examples
|
|
654
|
+
|
|
655
|
+
- Counter – [sandbox](https://codesandbox.io/p/sandbox/reactish-counter-z42qt7) | [source](https://github.com/szhsin/reactish-state/tree/master/examples/examples/counter)
|
|
656
|
+
- Todo app – [sandbox](https://codesandbox.io/s/reactish-todo-thyhbl) | [source](https://github.com/szhsin/reactish-state/tree/master/examples/examples/todo)
|
|
657
|
+
- Async – [sandbox](https://codesandbox.io/s/reactish-async-2cghkg) | [source](https://github.com/szhsin/reactish-state/tree/master/examples/examples/async)
|
|
658
|
+
|
|
591
659
|
# React 16/17 setup
|
|
592
660
|
|
|
593
661
|
When using this library with React 16/17, you must set up a shim since it doesn't include a native [useSyncExternalStore](https://react.dev/reference/react/useSyncExternalStore). We don't set up the shim by default to minimize the bundle size for React 18/19 users.
|
|
@@ -599,9 +667,3 @@ setReactShim(reactShim);
|
|
|
599
667
|
```
|
|
600
668
|
|
|
601
669
|
You only need to set it up once after your app launches, outside of React code. DO NOT call `setReactShim` within any React components.
|
|
602
|
-
|
|
603
|
-
# Examples
|
|
604
|
-
|
|
605
|
-
- Counter – [sandbox](https://codesandbox.io/p/sandbox/reactish-counter-z42qt7) | [source](https://github.com/szhsin/reactish-state/tree/master/examples/examples/counter)
|
|
606
|
-
- Todo app – [sandbox](https://codesandbox.io/s/reactish-todo-thyhbl) | [source](https://github.com/szhsin/reactish-state/tree/master/examples/examples/todo)
|
|
607
|
-
- Async – [sandbox](https://codesandbox.io/s/reactish-async-2cghkg) | [source](https://github.com/szhsin/reactish-state/tree/master/examples/examples/async)
|
|
@@ -7,7 +7,7 @@ const useSnapshot = ({
|
|
|
7
7
|
get
|
|
8
8
|
}) => {
|
|
9
9
|
if (process.env.NODE_ENV !== 'production' && !shim.useSyncExternalStore) {
|
|
10
|
-
throw new Error('[reactish-state] Shim setup is required for React 16/17.');
|
|
10
|
+
throw new Error('[reactish-state] Shim setup is required for React 16/17. See: https://github.com/szhsin/reactish-state/tree/master?tab=readme-ov-file#react-1617-setup');
|
|
11
11
|
}
|
|
12
12
|
return shim.useSyncExternalStore(subscribe, get, get);
|
|
13
13
|
};
|
|
@@ -5,7 +5,7 @@ const useSnapshot = ({
|
|
|
5
5
|
get
|
|
6
6
|
}) => {
|
|
7
7
|
if (process.env.NODE_ENV !== 'production' && !useSyncExternalStore) {
|
|
8
|
-
throw new Error('[reactish-state] Shim setup is required for React 16/17.');
|
|
8
|
+
throw new Error('[reactish-state] Shim setup is required for React 16/17. See: https://github.com/szhsin/reactish-state/tree/master?tab=readme-ov-file#react-1617-setup');
|
|
9
9
|
}
|
|
10
10
|
return useSyncExternalStore(subscribe, get, get);
|
|
11
11
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reactish-state",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
4
|
-
"description": "Simple, decentralized state management for React.",
|
|
3
|
+
"version": "1.0.0-alpha.3",
|
|
4
|
+
"description": "Simple, decentralized (atomic) state management for React.",
|
|
5
5
|
"author": "Zheng Song",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"lint:fix": "eslint --fix .",
|
|
38
38
|
"pret": "prettier -c .",
|
|
39
39
|
"pret:fix": "prettier -w .",
|
|
40
|
-
"build": "run-s pret
|
|
40
|
+
"build": "run-s pret clean types lint bundle",
|
|
41
41
|
"test": "jest",
|
|
42
42
|
"test:watch": "jest --watch",
|
|
43
43
|
"eg": "npm run dev --prefix examples"
|
package/types/vanilla/state.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Reactish, Setter, Config, Middleware } from '../common';
|
|
2
2
|
type ActionCreator<T, A> = ((set: Setter<T>, get: () => T) => A) | null | undefined;
|
|
3
|
-
|
|
3
|
+
type VanillaState<T> = Reactish<T> & {
|
|
4
4
|
set: Setter<T>;
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
};
|
|
6
|
+
type State<T, A> = Omit<A, keyof VanillaState<T>> & VanillaState<T>;
|
|
7
7
|
declare const createState: ({ middleware }?: {
|
|
8
8
|
middleware?: Middleware;
|
|
9
|
-
}) => <T, A>(initialValue: T, actionCreator?: ActionCreator<T, A>, config?: Config) => State<T, A
|
|
10
|
-
declare const state: <T, A>(initialValue: T, actionCreator?: ActionCreator<T, A>, config?: Config) => State<T, A
|
|
9
|
+
}) => <T, A>(initialValue: T, actionCreator?: ActionCreator<T, A>, config?: Config) => State<T, A>;
|
|
10
|
+
declare const state: <T, A>(initialValue: T, actionCreator?: ActionCreator<T, A>, config?: Config) => State<T, A>;
|
|
11
11
|
export type { State, ActionCreator };
|
|
12
12
|
export { state, createState };
|