@t8/react-store 1.2.0 → 1.2.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 +10 -4
- package/dist/index.js +30 -16
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@t8/react-store)   
|
|
6
6
|
|
|
7
|
-
**Why?** To
|
|
7
|
+
**Why?** To have an easy-to-use state management lib for React apps requiring least effort to migrate from local state and to quickly set up shared state from scratch, whether with SSR or without. Other approaches, including Redux Toolkit, Zustand, Jotai, MobX, invariably depart from this picture to varying degrees.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
This picture is achieved here by (1) having a simple API introducing as few new entities as possible, (2) closely following the React's `useState()` pattern of initializing and manipulating the state to avoid boilerplate and sizable rewrites in the common task of migration from local state to shared state, (3) working smoothly with SSR with regular React Contexts without requiring a specifically designed setup and without internally making use of global stores or other global variables by default.
|
|
10
10
|
|
|
11
11
|
<!-- docsgen-show-start --
|
|
12
12
|
```diff
|
|
@@ -68,6 +68,8 @@ Moving the local state to the full-fledged shared state:
|
|
|
68
68
|
|
|
69
69
|
🔹 The optional `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.
|
|
70
70
|
|
|
71
|
+
🔹 With SSR, it's common practice to put shared values into React Context rather than module-level variables to avoid cross-request data sharing. The same applies to stores, see an example in the [Sharing state via Context](#sharing-state-via-context) section below.
|
|
72
|
+
|
|
71
73
|
🔹 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.
|
|
72
74
|
|
|
73
75
|
## Single store or multiple stores
|
|
@@ -103,7 +105,7 @@ let ItemCard = ({ id }) => {
|
|
|
103
105
|
|
|
104
106
|
While `useStore(itemStore)` in this component would trigger a re-render in response to any changes in the `itemStore` (which can be fine with a small store), with `useStore(itemStore, shouldUpdate)` the `ItemCard` component has a more targeted subscription to the store: in this example, a re-render will only be triggered if the `revision` property of the item with the given `id` has changed.
|
|
105
107
|
|
|
106
|
-
##
|
|
108
|
+
## Sharing state via Context
|
|
107
109
|
|
|
108
110
|
Shared state can be provided to the app by means of a regular React Context provider:
|
|
109
111
|
|
|
@@ -177,10 +179,14 @@ A standalone store initialized outside a component can be used by the component
|
|
|
177
179
|
|
|
178
180
|
## Persistence across page reloads
|
|
179
181
|
|
|
182
|
+
Replacing `new Store(data)` with `new PersistentStore(data, storageKey)` as shown below gets the store's state value initially restored from and saved whenever updated to `storageKey` in `localStorage`. (Pass `{ session: true }` as the `options` parameter of `new PersistentStore(data, storageKey, options?)` to use `sessionStorage` instead of `localStorage`.) Otherwise, persistent stores work pretty much like regular stores described above.
|
|
183
|
+
|
|
180
184
|
```js
|
|
181
185
|
import { PersistentStore } from "@t8/react-store";
|
|
182
186
|
|
|
183
187
|
let counterStore = new PersistentStore(0, "counter");
|
|
184
188
|
```
|
|
185
189
|
|
|
186
|
-
|
|
190
|
+
🔹 The way data gets saved to and restored from a browser storage entry (including filtering out certain data or otherwise rearranging the saved data) can be redefined by setting `options.serialize` and `options.deserialize` in `new PersistentStore(data, storageKey, options?)`. By default, these options act like `JSON.stringify()` and `JSON.parse()` respectively.
|
|
191
|
+
|
|
192
|
+
🔹 `PersistentStore` skips interaction with the browser storage in non-browser environments, which makes it equally usable with SSR.
|
package/dist/index.js
CHANGED
|
@@ -35,32 +35,43 @@ var Store = class {
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
// node_modules/@t8/store/src/PersistentStore.ts
|
|
38
|
-
function getStorage(session) {
|
|
38
|
+
function getStorage(session = false) {
|
|
39
39
|
if (typeof window === "undefined") return;
|
|
40
40
|
return session ? sessionStorage : localStorage;
|
|
41
41
|
}
|
|
42
42
|
var PersistentStore = class extends Store {
|
|
43
43
|
storageKey;
|
|
44
|
-
|
|
44
|
+
options;
|
|
45
45
|
synced = false;
|
|
46
46
|
/**
|
|
47
47
|
* Creates an instance of the container for data persistent across page
|
|
48
48
|
* reloads.
|
|
49
49
|
*
|
|
50
50
|
* The store data is saved to and restored from the given `storageKey`
|
|
51
|
-
* either of `localStorage`
|
|
52
|
-
*
|
|
53
|
-
*
|
|
51
|
+
* either of `localStorage` (by default) or `sessionStorage` (if `options.session`
|
|
52
|
+
* is set to `true`). Interaction with the browser storage is skipped in
|
|
53
|
+
* non-browser environments.
|
|
54
54
|
*
|
|
55
55
|
* @example
|
|
56
56
|
* ```js
|
|
57
57
|
* let counterStore = new PersistentStore(0, "counter");
|
|
58
58
|
* ```
|
|
59
|
+
*
|
|
60
|
+
* The way data gets saved to and restored from a browser storage entry
|
|
61
|
+
* (including filtering out certain data or otherwise rearranging the
|
|
62
|
+
* saved data) can be overridden by setting `options.serialize` and
|
|
63
|
+
* `options.deserialize`. By default, they are `JSON.stringify()` and
|
|
64
|
+
* `JSON.parse()`.
|
|
59
65
|
*/
|
|
60
|
-
constructor(data, storageKey,
|
|
66
|
+
constructor(data, storageKey, options) {
|
|
61
67
|
super(data);
|
|
62
68
|
this.storageKey = storageKey;
|
|
63
|
-
this.
|
|
69
|
+
this.options = {
|
|
70
|
+
session: false,
|
|
71
|
+
serialize: (data2) => JSON.stringify(data2),
|
|
72
|
+
deserialize: (content) => JSON.parse(content),
|
|
73
|
+
...options
|
|
74
|
+
};
|
|
64
75
|
this.onUpdate(() => {
|
|
65
76
|
if (this.synced) this.save();
|
|
66
77
|
});
|
|
@@ -69,10 +80,11 @@ var PersistentStore = class extends Store {
|
|
|
69
80
|
* Saves the store state value to the browser storage.
|
|
70
81
|
*/
|
|
71
82
|
save() {
|
|
72
|
-
let storage = getStorage(this.session);
|
|
73
|
-
|
|
83
|
+
let storage = getStorage(this.options?.session);
|
|
84
|
+
let serialize = this.options?.serialize;
|
|
85
|
+
if (this.synced && storage && typeof serialize === "function") {
|
|
74
86
|
try {
|
|
75
|
-
storage.setItem(this.storageKey,
|
|
87
|
+
storage.setItem(this.storageKey, serialize(this.state));
|
|
76
88
|
} catch {
|
|
77
89
|
}
|
|
78
90
|
}
|
|
@@ -81,18 +93,20 @@ var PersistentStore = class extends Store {
|
|
|
81
93
|
* Signals the store to read the state value from the browser storage.
|
|
82
94
|
*/
|
|
83
95
|
sync() {
|
|
84
|
-
let storage = getStorage(this.session);
|
|
85
|
-
let
|
|
86
|
-
|
|
96
|
+
let storage = getStorage(this.options?.session);
|
|
97
|
+
let deserialize = this.options?.deserialize;
|
|
98
|
+
let serializedState = null;
|
|
99
|
+
if (storage && typeof deserialize === "function") {
|
|
87
100
|
try {
|
|
88
|
-
|
|
89
|
-
if (
|
|
101
|
+
serializedState = storage.getItem(this.storageKey);
|
|
102
|
+
if (serializedState !== null)
|
|
103
|
+
this.setState(deserialize(serializedState, this.state));
|
|
90
104
|
} catch {
|
|
91
105
|
}
|
|
92
106
|
}
|
|
93
107
|
if (!this.synced) {
|
|
94
108
|
this.synced = true;
|
|
95
|
-
if (
|
|
109
|
+
if (serializedState === null) this.save();
|
|
96
110
|
}
|
|
97
111
|
}
|
|
98
112
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@t8/react-store",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Small React app state management lib aligned with React's state pattern, condensed to the essentials",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -45,6 +45,6 @@
|
|
|
45
45
|
"typescript": "^5.9.3"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@t8/store": "^1.3.
|
|
48
|
+
"@t8/store": "^1.3.3"
|
|
49
49
|
}
|
|
50
50
|
}
|