reactish-state 1.0.0-alpha.1 → 1.0.0-alpha.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
|
@@ -43,7 +43,7 @@ countState.set(10);
|
|
|
43
43
|
console.log(countState.get()); // Print 10
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
### A state can also have actions bound to it
|
|
46
|
+
### A state can also have custom actions bound to it
|
|
47
47
|
|
|
48
48
|
```js
|
|
49
49
|
const countState = state(0, (set, get) => ({
|
|
@@ -55,8 +55,8 @@ const countState = state(0, (set, get) => ({
|
|
|
55
55
|
decrease: () => set(get() - 1)
|
|
56
56
|
}));
|
|
57
57
|
|
|
58
|
-
// Use the actions
|
|
59
|
-
countState.
|
|
58
|
+
// Use the custom actions
|
|
59
|
+
countState.increase();
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
### `selector` can create derived state
|
|
@@ -90,9 +90,10 @@ const Example = () => {
|
|
|
90
90
|
|
|
91
91
|
return (
|
|
92
92
|
<h1>
|
|
93
|
+
{/* The return values of `useSnapshot` are used for rendering */}
|
|
93
94
|
{count} {triple}
|
|
94
|
-
{/* Update the state using the actions bound to it */}
|
|
95
|
-
<button onClick={() => countState.
|
|
95
|
+
{/* Update the state using the custom actions bound to it */}
|
|
96
|
+
<button onClick={() => countState.increase()}>Increase</button>
|
|
96
97
|
{/* Or update the state using the `set` method directly */}
|
|
97
98
|
<button onClick={() => countState.set((i) => i - 1)}>Decrease</button>
|
|
98
99
|
<button onClick={() => countState.set(0)}>Reset</button>
|
|
@@ -232,11 +233,14 @@ You can also create async actions bound to a state:
|
|
|
232
233
|
|
|
233
234
|
```js
|
|
234
235
|
const todosState = state([], (set) => ({
|
|
235
|
-
|
|
236
|
-
const response = await fetch(url);
|
|
236
|
+
fetchData: async () => {
|
|
237
|
+
const response = await fetch(/* some url */);
|
|
237
238
|
set(await response.json());
|
|
238
239
|
}
|
|
239
240
|
}));
|
|
241
|
+
|
|
242
|
+
// Use the async action
|
|
243
|
+
await todosState.fetchData();
|
|
240
244
|
```
|
|
241
245
|
|
|
242
246
|
## Accessing other state or selectors inside actions
|
|
@@ -293,7 +297,7 @@ const countState = state(0, (set) => ({
|
|
|
293
297
|
increase: () => set((count) => count + 1),
|
|
294
298
|
reset: () => set(0)
|
|
295
299
|
}));
|
|
296
|
-
const { increase, reset } = countState
|
|
300
|
+
const { increase, reset } = countState;
|
|
297
301
|
|
|
298
302
|
const Example = () => {
|
|
299
303
|
const count = useSnapshot(countState);
|
|
@@ -372,7 +376,7 @@ const countState = state(0, (set) => ({
|
|
|
372
376
|
dispatch: (action) => set((state) => reducer(state, action), action)
|
|
373
377
|
}));
|
|
374
378
|
|
|
375
|
-
const { dispatch } = countState
|
|
379
|
+
const { dispatch } = countState;
|
|
376
380
|
dispatch({ type: "INCREASE", by: 10 });
|
|
377
381
|
dispatch({ type: "DECREASE", by: 7 });
|
|
378
382
|
console.log(countState.get()); // Print 3
|
|
@@ -401,7 +405,7 @@ const countState = state(0, (set) => ({
|
|
|
401
405
|
}));
|
|
402
406
|
|
|
403
407
|
countState.set(99); // Print "New state 99"
|
|
404
|
-
countState.
|
|
408
|
+
countState.increase(); // Print "New state 100"
|
|
405
409
|
|
|
406
410
|
// The same `state` function can be reused,
|
|
407
411
|
// so you don't need to set up the middleware again
|
|
@@ -473,8 +477,8 @@ const todos = state([], (set) => ({
|
|
|
473
477
|
}));
|
|
474
478
|
|
|
475
479
|
// Use the actions
|
|
476
|
-
todos.
|
|
477
|
-
todos.
|
|
480
|
+
todos.add("Shop groceries");
|
|
481
|
+
todos.toggle(1);
|
|
478
482
|
```
|
|
479
483
|
|
|
480
484
|
## Redux devtools middleware
|
|
@@ -588,6 +592,52 @@ const selector = createSelector({ plugin: reduxDevtools() });
|
|
|
588
592
|
// Then use the `selector` as usual...
|
|
589
593
|
```
|
|
590
594
|
|
|
595
|
+
# TypeScript usage
|
|
596
|
+
|
|
597
|
+
The API relies on type inference to correctly infer the types for both the value and actions of the state. There are two scenarios:
|
|
598
|
+
|
|
599
|
+
## I. The type of state can be inferred from its initial value
|
|
600
|
+
|
|
601
|
+
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.
|
|
602
|
+
|
|
603
|
+
## II. The type of state cannot be inferred from its initial value
|
|
604
|
+
|
|
605
|
+
In this case, you have three options:
|
|
606
|
+
|
|
607
|
+
### 1. Use a type assertion to specify a more specific type for the initial value:
|
|
608
|
+
|
|
609
|
+
```ts
|
|
610
|
+
const myTodos = state([] as string[], (set) => ({
|
|
611
|
+
add: (newTodo: string) => set((todos) => [...todos, newTodo])
|
|
612
|
+
}));
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
This is the simplest approach since the types for custom actions will be automatically inferred.
|
|
616
|
+
|
|
617
|
+
### 2. Declare the initial value separately with a specific type:
|
|
618
|
+
|
|
619
|
+
```ts
|
|
620
|
+
const initialValue: string[] = [];
|
|
621
|
+
const myTodos = state(initialValue, (set) => ({
|
|
622
|
+
add: (newTodo: string) => set((todos) => [...todos, newTodo])
|
|
623
|
+
}));
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
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.
|
|
627
|
+
|
|
628
|
+
### 3. Specify type parameters explicitly:
|
|
629
|
+
|
|
630
|
+
```ts
|
|
631
|
+
const myTodos = state<string[], { add: (newTodo: string) => void }>(
|
|
632
|
+
[],
|
|
633
|
+
(set) => ({
|
|
634
|
+
add: (newTodo) => set((todos) => [...todos, newTodo])
|
|
635
|
+
})
|
|
636
|
+
);
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
However, if you choose this method, you need to specify the types for both the state value and actions.
|
|
640
|
+
|
|
591
641
|
# React 16/17 setup
|
|
592
642
|
|
|
593
643
|
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.
|
|
@@ -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
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 };
|