@t8/react-store 1.0.23 → 1.0.25
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 +54 -30
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,25 +16,24 @@ Installation: `npm i @t8/react-store`
|
|
|
16
16
|
Moving the local state to the full-fledged shared state:
|
|
17
17
|
|
|
18
18
|
```diff
|
|
19
|
-
import { createContext, useContext } from "react";
|
|
20
19
|
+ import { Store, useStore } from "@t8/react-store";
|
|
21
20
|
+
|
|
22
|
-
+ let
|
|
21
|
+
+ let counterStore = new Store(0);
|
|
23
22
|
|
|
24
23
|
let Counter = () => {
|
|
25
24
|
- let [counter, setCounter] = useState(0);
|
|
26
|
-
+ let [counter, setCounter] = useStore(
|
|
25
|
+
+ let [counter, setCounter] = useStore(counterStore);
|
|
27
26
|
|
|
28
27
|
let handleClick = () => {
|
|
29
28
|
setCounter(value => value + 1);
|
|
30
29
|
};
|
|
31
30
|
|
|
32
|
-
return <button onClick={handleClick}
|
|
31
|
+
return <button onClick={handleClick}>+ {counter}</button>;
|
|
33
32
|
};
|
|
34
33
|
|
|
35
34
|
let ResetButton = () => {
|
|
36
35
|
- let [, setCounter] = useState(0);
|
|
37
|
-
+ let [, setCounter] = useStore(
|
|
36
|
+
+ let [, setCounter] = useStore(counterStore, false);
|
|
38
37
|
|
|
39
38
|
let handleClick = () => {
|
|
40
39
|
setCounter(0);
|
|
@@ -46,27 +45,20 @@ Moving the local state to the full-fledged shared state:
|
|
|
46
45
|
let App = () => <><Counter/>{" "}<ResetButton/></>;
|
|
47
46
|
```
|
|
48
47
|
|
|
49
|
-
[Live counter demo](https://codesandbox.io/p/sandbox/
|
|
48
|
+
[Live counter demo](https://codesandbox.io/p/sandbox/szhdnw?file=%252Fsrc%252FApp.tsx)<br>
|
|
50
49
|
[Tic-tac-toe](https://codesandbox.io/p/sandbox/tq852v?file=%252Fsrc%252FApp.tsx)
|
|
51
50
|
|
|
52
51
|
🔹 The shared state setup shown above is very similar to `useState()` allowing for quick migration from local state to shared state or the other way around.
|
|
53
52
|
|
|
54
53
|
🔹 The `false` parameter in `useStore(store, false)` (as in `<ResetButton>` above) tells the hook not to subscribe the component to tracking the store state updates. The common use case is when a component makes use of the store state setter without using the store state value.
|
|
55
54
|
|
|
56
|
-
🔹
|
|
55
|
+
🔹 Similarly to instances of the built-in data container classes, such as `Set` and `Map`, stores are created as `new Store(data)` rather than with a factory function.
|
|
57
56
|
|
|
58
57
|
## Single store or multiple stores
|
|
59
58
|
|
|
60
|
-
An application can have as many stores as needed
|
|
59
|
+
An application can have as many stores as needed.
|
|
61
60
|
|
|
62
|
-
|
|
63
|
-
let AppContext = createContext({
|
|
64
|
-
users: new Store(/* ... */),
|
|
65
|
-
items: new Store(/* ... */),
|
|
66
|
-
});
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
🔹 Splitting data into multiple stores helps maintain more targeted subscriptions to data changes in components.
|
|
61
|
+
🔹 Splitting data into multiple stores is one of the strategies to maintain more targeted subscriptions to data changes in components. The other strategy is filtering store updates at the component level, which is discussed below.
|
|
70
62
|
|
|
71
63
|
## Filtering store updates
|
|
72
64
|
|
|
@@ -76,7 +68,7 @@ When only the store state setter is required, without the store state value, we
|
|
|
76
68
|
let [, setState] = useState(store, false);
|
|
77
69
|
```
|
|
78
70
|
|
|
79
|
-
Apart from a boolean, `useStore(store, shouldUpdate)`
|
|
71
|
+
Apart from a boolean, `useStore(store, shouldUpdate)` accepts a function of `(nextState, prevState) => boolean` as the second parameter to filter store updates to respond to:
|
|
80
72
|
|
|
81
73
|
```jsx
|
|
82
74
|
let ItemCard = ({ id }) => {
|
|
@@ -86,10 +78,7 @@ let ItemCard = ({ id }) => {
|
|
|
86
78
|
return nextItems[id].revision !== prevItems[id].revision;
|
|
87
79
|
}, [id]);
|
|
88
80
|
|
|
89
|
-
let [items, setItems] = useStore(
|
|
90
|
-
useContext(AppContext).items,
|
|
91
|
-
hasRelevantUpdates,
|
|
92
|
-
);
|
|
81
|
+
let [items, setItems] = useStore(itemStore, hasRelevantUpdates);
|
|
93
82
|
|
|
94
83
|
return (
|
|
95
84
|
// Content
|
|
@@ -101,15 +90,50 @@ let ItemCard = ({ id }) => {
|
|
|
101
90
|
|
|
102
91
|
Shared state can be provided to the app by means of a regular React Context provider:
|
|
103
92
|
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
93
|
+
```ts
|
|
94
|
+
import { createContext } from "react";
|
|
95
|
+
|
|
96
|
+
export let AppContext = createContext(new Store(0));
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
let App = () => (
|
|
101
|
+
<AppContext.Provider value={new Store(42)}>
|
|
102
|
+
<PlusButton/>{" "}<Display/>
|
|
103
|
+
</AppContext.Provider>
|
|
104
|
+
);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
let Counter = () => {
|
|
109
|
+
let [counter, setCounter] = useStore(useContext(AppContext));
|
|
110
|
+
|
|
111
|
+
// Rendering
|
|
112
|
+
};
|
|
111
113
|
```
|
|
112
114
|
|
|
115
|
+
[Live counter demo with Context](https://codesandbox.io/p/sandbox/rtng37?file=%2Fsrc%2FPlusButton.jsx)
|
|
116
|
+
|
|
117
|
+
🔹 In a multi-store setup, stores can be located in a single Context or split across multiple Contexts, just like any application data.
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
// Multiple stores in a single Context
|
|
121
|
+
let AppContext = createContext({
|
|
122
|
+
users: new Store(/* ... */),
|
|
123
|
+
items: new Store(/* ... */),
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```jsx
|
|
128
|
+
let ItemCard = ({ id }) => {
|
|
129
|
+
let [items, setItems] = useStore(useContext(AppContext).items);
|
|
130
|
+
|
|
131
|
+
// Rendering
|
|
132
|
+
};
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
🔹 Note that updating the store state doesn't change the store reference sitting in the React Context and therefore doesn't cause updates of the entire Context. Only the components subscribed to updates in the particular store by means of `useStore(store)` will be notified to re-render.
|
|
136
|
+
|
|
113
137
|
## Store data
|
|
114
138
|
|
|
115
139
|
A store can contain data of any type.
|
|
@@ -128,6 +152,6 @@ Immer can be used with `useStore()` just the same way as [with `useState()`](htt
|
|
|
128
152
|
|
|
129
153
|
The ready-to-use hook from the [T8 React Pending](https://github.com/t8js/react-pending) package helps manage shared async action state without disturbing the app's state management and actions' code.
|
|
130
154
|
|
|
131
|
-
##
|
|
155
|
+
## Remount-persistent state
|
|
132
156
|
|
|
133
|
-
A store initialized outside a component can be used
|
|
157
|
+
A standalone store initialized outside a component can be used by the component as remount-persistent state, whether used by other components or not.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t8/react-store",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.25",
|
|
4
4
|
"description": "Concise shared state management for React apps",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -44,6 +44,6 @@
|
|
|
44
44
|
"typescript": "^5.9.3"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@t8/store": "^1.1.
|
|
47
|
+
"@t8/store": "^1.1.5"
|
|
48
48
|
}
|
|
49
49
|
}
|