use-json-localstorage 1.2.0 → 1.3.0
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 +180 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# use-json-localstorage
|
|
2
|
+
|
|
3
|
+
A tiny, **React 18+ safe** localStorage utility built on top of `useSyncExternalStore`.
|
|
4
|
+
|
|
5
|
+
> Most `useLocalStorage` hooks rely on `useEffect + useState`, which can be unsafe in concurrent rendering.
|
|
6
|
+
> This library follows the **official React pattern** for subscribing to external stores.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Why
|
|
11
|
+
|
|
12
|
+
React 18 introduced **concurrent rendering**. In this environment, the common pattern below is no longer safe:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
store.subscribe(setState);
|
|
17
|
+
}, []);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This can lead to:
|
|
21
|
+
|
|
22
|
+
* tearing (inconsistent state during render)
|
|
23
|
+
* unexpected double renders in Strict Mode
|
|
24
|
+
* subtle bugs that are hard to reproduce
|
|
25
|
+
|
|
26
|
+
React provides a solution for this exact problem:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
useSyncExternalStore
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`use-json-localstorage` is a small abstraction that applies this API to `localStorage`.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
* ✅ React 18+ safe (`useSyncExternalStore`)
|
|
39
|
+
* ✅ JSON-based persistence (via `superjson`)
|
|
40
|
+
* ✅ Subscribable localStorage updates
|
|
41
|
+
* ✅ Type-safe API
|
|
42
|
+
* ✅ Tiny & opinionated
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Non-goals
|
|
47
|
+
|
|
48
|
+
This library **does NOT aim to be**:
|
|
49
|
+
|
|
50
|
+
* a global state manager (Redux, Zustand, etc.)
|
|
51
|
+
* an ORM or database abstraction
|
|
52
|
+
* a complex query engine
|
|
53
|
+
* a replacement for server-side persistence
|
|
54
|
+
|
|
55
|
+
It is intentionally small.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install use-json-localstorage
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
React 18 or higher is required.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Basic Usage
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { useLocalStorageValue } from 'use-json-localstorage';
|
|
73
|
+
|
|
74
|
+
function Counter() {
|
|
75
|
+
const count = useLocalStorageValue('count', 0);
|
|
76
|
+
|
|
77
|
+
return <div>{count}</div>;
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
* `count` is read from `localStorage`
|
|
82
|
+
* updates are subscribed via `useSyncExternalStore`
|
|
83
|
+
* React always receives a consistent snapshot
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Writing Values
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { getRuntimeLocalStorage } from 'use-json-localstorage';
|
|
91
|
+
|
|
92
|
+
getRuntimeLocalStorage().set('count', 1);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
All subscribers of the same key will be notified.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Supported Data Types
|
|
100
|
+
|
|
101
|
+
Serialization is handled by `superjson`, which means the following are supported:
|
|
102
|
+
|
|
103
|
+
* `Object`
|
|
104
|
+
* `Array`
|
|
105
|
+
* `Date`
|
|
106
|
+
* `Map`
|
|
107
|
+
* `Set`
|
|
108
|
+
* `BigInt`
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## How It Works
|
|
113
|
+
|
|
114
|
+
At a high level:
|
|
115
|
+
|
|
116
|
+
1. `localStorage` is wrapped as an **evented external store**
|
|
117
|
+
2. React subscribes via `useSyncExternalStore`
|
|
118
|
+
3. Updates trigger a re-render using a fresh snapshot
|
|
119
|
+
|
|
120
|
+
```txt
|
|
121
|
+
localStorage
|
|
122
|
+
↓
|
|
123
|
+
event emitter
|
|
124
|
+
↓
|
|
125
|
+
useSyncExternalStore
|
|
126
|
+
↓
|
|
127
|
+
React render
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
This follows the same pattern used internally by modern state libraries.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Example: Mutation-style Update
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
getRuntimeLocalStorage().set('user', {
|
|
138
|
+
id: '1',
|
|
139
|
+
name: 'Dohyeon',
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
You can build schema-based mutation helpers on top of this primitive.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## SSR
|
|
148
|
+
|
|
149
|
+
This library is **client-only**.
|
|
150
|
+
|
|
151
|
+
* No SSR persistence
|
|
152
|
+
* No server synchronization
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
MIT
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Motivation
|
|
163
|
+
|
|
164
|
+
This project was created to explore:
|
|
165
|
+
|
|
166
|
+
* React 18 concurrency-safe patterns
|
|
167
|
+
* External store design
|
|
168
|
+
* A minimal, explainable alternative to ad-hoc `useLocalStorage` hooks
|
|
169
|
+
|
|
170
|
+
If you are building UI infrastructure or headless utilities, this pattern may be useful.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Credits
|
|
175
|
+
|
|
176
|
+
Inspired by the React 18 RFC and the design of modern state libraries.
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT
|