@usefy/use-session-storage 0.0.16 → 0.0.18
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 +117 -91
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,11 +55,11 @@
|
|
|
55
55
|
|
|
56
56
|
### localStorage vs sessionStorage
|
|
57
57
|
|
|
58
|
-
| Feature
|
|
59
|
-
|
|
60
|
-
| Data persistence | Until explicitly cleared | Until tab closes
|
|
61
|
-
| Tab sharing
|
|
62
|
-
| Best for
|
|
58
|
+
| Feature | localStorage | sessionStorage |
|
|
59
|
+
| ---------------- | ------------------------ | ------------------------- |
|
|
60
|
+
| Data persistence | Until explicitly cleared | Until tab closes |
|
|
61
|
+
| Tab sharing | Shared across all tabs | Isolated per tab |
|
|
62
|
+
| Best for | User preferences, themes | Form drafts, wizard steps |
|
|
63
63
|
|
|
64
64
|
---
|
|
65
65
|
|
|
@@ -93,28 +93,37 @@ This package requires React 18 or 19:
|
|
|
93
93
|
## Quick Start
|
|
94
94
|
|
|
95
95
|
```tsx
|
|
96
|
-
import { useSessionStorage } from
|
|
96
|
+
import { useSessionStorage } from "@usefy/use-session-storage";
|
|
97
97
|
|
|
98
98
|
function CheckoutForm() {
|
|
99
|
-
const [formData, setFormData, clearForm] = useSessionStorage(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
99
|
+
const [formData, setFormData, clearForm] = useSessionStorage(
|
|
100
|
+
"checkout-form",
|
|
101
|
+
{
|
|
102
|
+
name: "",
|
|
103
|
+
email: "",
|
|
104
|
+
address: "",
|
|
105
|
+
}
|
|
106
|
+
);
|
|
104
107
|
|
|
105
108
|
return (
|
|
106
109
|
<form>
|
|
107
110
|
<input
|
|
108
111
|
value={formData.name}
|
|
109
|
-
onChange={(e) =>
|
|
112
|
+
onChange={(e) =>
|
|
113
|
+
setFormData((prev) => ({ ...prev, name: e.target.value }))
|
|
114
|
+
}
|
|
110
115
|
placeholder="Name"
|
|
111
116
|
/>
|
|
112
117
|
<input
|
|
113
118
|
value={formData.email}
|
|
114
|
-
onChange={(e) =>
|
|
119
|
+
onChange={(e) =>
|
|
120
|
+
setFormData((prev) => ({ ...prev, email: e.target.value }))
|
|
121
|
+
}
|
|
115
122
|
placeholder="Email"
|
|
116
123
|
/>
|
|
117
|
-
<button type="button" onClick={clearForm}>
|
|
124
|
+
<button type="button" onClick={clearForm}>
|
|
125
|
+
Clear Form
|
|
126
|
+
</button>
|
|
118
127
|
</form>
|
|
119
128
|
);
|
|
120
129
|
}
|
|
@@ -130,27 +139,27 @@ A hook that persists state in sessionStorage for the duration of the browser ses
|
|
|
130
139
|
|
|
131
140
|
#### Parameters
|
|
132
141
|
|
|
133
|
-
| Parameter
|
|
134
|
-
|
|
135
|
-
| `key`
|
|
136
|
-
| `initialValue` | `T \| () => T`
|
|
137
|
-
| `options`
|
|
142
|
+
| Parameter | Type | Description |
|
|
143
|
+
| -------------- | ----------------------------- | ------------------------------------------ |
|
|
144
|
+
| `key` | `string` | The sessionStorage key |
|
|
145
|
+
| `initialValue` | `T \| () => T` | Initial value or lazy initializer function |
|
|
146
|
+
| `options` | `UseSessionStorageOptions<T>` | Configuration options |
|
|
138
147
|
|
|
139
148
|
#### Options
|
|
140
149
|
|
|
141
|
-
| Option
|
|
142
|
-
|
|
143
|
-
| `serializer`
|
|
144
|
-
| `deserializer` | `(value: string) => T`
|
|
145
|
-
| `onError`
|
|
150
|
+
| Option | Type | Default | Description |
|
|
151
|
+
| -------------- | ------------------------ | ---------------- | ---------------------------- |
|
|
152
|
+
| `serializer` | `(value: T) => string` | `JSON.stringify` | Custom serializer function |
|
|
153
|
+
| `deserializer` | `(value: string) => T` | `JSON.parse` | Custom deserializer function |
|
|
154
|
+
| `onError` | `(error: Error) => void` | — | Callback for error handling |
|
|
146
155
|
|
|
147
156
|
#### Returns `[T, SetValue<T>, RemoveValue]`
|
|
148
157
|
|
|
149
|
-
| Index | Type
|
|
150
|
-
|
|
151
|
-
| `[0]` | `T`
|
|
152
|
-
| `[1]` | `Dispatch<SetStateAction<T>>` | Function to update value (same as useState)
|
|
153
|
-
| `[2]` | `() => void`
|
|
158
|
+
| Index | Type | Description |
|
|
159
|
+
| ----- | ----------------------------- | --------------------------------------------- |
|
|
160
|
+
| `[0]` | `T` | Current stored value |
|
|
161
|
+
| `[1]` | `Dispatch<SetStateAction<T>>` | Function to update value (same as useState) |
|
|
162
|
+
| `[2]` | `() => void` | Function to remove value and reset to initial |
|
|
154
163
|
|
|
155
164
|
---
|
|
156
165
|
|
|
@@ -159,13 +168,13 @@ A hook that persists state in sessionStorage for the duration of the browser ses
|
|
|
159
168
|
### Multi-Step Wizard
|
|
160
169
|
|
|
161
170
|
```tsx
|
|
162
|
-
import { useSessionStorage } from
|
|
171
|
+
import { useSessionStorage } from "@usefy/use-session-storage";
|
|
163
172
|
|
|
164
173
|
function SignupWizard() {
|
|
165
|
-
const [step, setStep] = useSessionStorage(
|
|
166
|
-
const [formData, setFormData, resetForm] = useSessionStorage(
|
|
167
|
-
email:
|
|
168
|
-
password:
|
|
174
|
+
const [step, setStep] = useSessionStorage("signup-step", 1);
|
|
175
|
+
const [formData, setFormData, resetForm] = useSessionStorage("signup-data", {
|
|
176
|
+
email: "",
|
|
177
|
+
password: "",
|
|
169
178
|
profile: {},
|
|
170
179
|
});
|
|
171
180
|
|
|
@@ -193,7 +202,9 @@ function SignupWizard() {
|
|
|
193
202
|
{step === 2 && (
|
|
194
203
|
<PasswordStep
|
|
195
204
|
value={formData.password}
|
|
196
|
-
onChange={(password) =>
|
|
205
|
+
onChange={(password) =>
|
|
206
|
+
setFormData((prev) => ({ ...prev, password }))
|
|
207
|
+
}
|
|
197
208
|
onBack={handleBack}
|
|
198
209
|
onNext={handleNext}
|
|
199
210
|
/>
|
|
@@ -215,12 +226,12 @@ function SignupWizard() {
|
|
|
215
226
|
### Form Draft (Auto-Restore)
|
|
216
227
|
|
|
217
228
|
```tsx
|
|
218
|
-
import { useSessionStorage } from
|
|
229
|
+
import { useSessionStorage } from "@usefy/use-session-storage";
|
|
219
230
|
|
|
220
231
|
function ContactForm() {
|
|
221
|
-
const [draft, setDraft, clearDraft] = useSessionStorage(
|
|
222
|
-
subject:
|
|
223
|
-
message:
|
|
232
|
+
const [draft, setDraft, clearDraft] = useSessionStorage("contact-draft", {
|
|
233
|
+
subject: "",
|
|
234
|
+
message: "",
|
|
224
235
|
});
|
|
225
236
|
|
|
226
237
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
@@ -233,17 +244,23 @@ function ContactForm() {
|
|
|
233
244
|
<form onSubmit={handleSubmit}>
|
|
234
245
|
<input
|
|
235
246
|
value={draft.subject}
|
|
236
|
-
onChange={(e) =>
|
|
247
|
+
onChange={(e) =>
|
|
248
|
+
setDraft((prev) => ({ ...prev, subject: e.target.value }))
|
|
249
|
+
}
|
|
237
250
|
placeholder="Subject"
|
|
238
251
|
/>
|
|
239
252
|
<textarea
|
|
240
253
|
value={draft.message}
|
|
241
|
-
onChange={(e) =>
|
|
254
|
+
onChange={(e) =>
|
|
255
|
+
setDraft((prev) => ({ ...prev, message: e.target.value }))
|
|
256
|
+
}
|
|
242
257
|
placeholder="Message"
|
|
243
258
|
/>
|
|
244
259
|
<p className="hint">Your draft is auto-saved in this tab</p>
|
|
245
260
|
<button type="submit">Send</button>
|
|
246
|
-
<button type="button" onClick={clearDraft}>
|
|
261
|
+
<button type="button" onClick={clearDraft}>
|
|
262
|
+
Discard
|
|
263
|
+
</button>
|
|
247
264
|
</form>
|
|
248
265
|
);
|
|
249
266
|
}
|
|
@@ -252,7 +269,7 @@ function ContactForm() {
|
|
|
252
269
|
### Shopping Cart (Per-Tab)
|
|
253
270
|
|
|
254
271
|
```tsx
|
|
255
|
-
import { useSessionStorage } from
|
|
272
|
+
import { useSessionStorage } from "@usefy/use-session-storage";
|
|
256
273
|
|
|
257
274
|
interface CartItem {
|
|
258
275
|
id: string;
|
|
@@ -261,7 +278,10 @@ interface CartItem {
|
|
|
261
278
|
}
|
|
262
279
|
|
|
263
280
|
function TabCart() {
|
|
264
|
-
const [cart, setCart, clearCart] = useSessionStorage<CartItem[]>(
|
|
281
|
+
const [cart, setCart, clearCart] = useSessionStorage<CartItem[]>(
|
|
282
|
+
"tab-cart",
|
|
283
|
+
[]
|
|
284
|
+
);
|
|
265
285
|
|
|
266
286
|
const addItem = (product: Product) => {
|
|
267
287
|
setCart((prev) => {
|
|
@@ -290,10 +310,13 @@ function TabCart() {
|
|
|
290
310
|
### Temporary Auth Token
|
|
291
311
|
|
|
292
312
|
```tsx
|
|
293
|
-
import { useSessionStorage } from
|
|
313
|
+
import { useSessionStorage } from "@usefy/use-session-storage";
|
|
294
314
|
|
|
295
315
|
function ProtectedPage() {
|
|
296
|
-
const [token, setToken, clearToken] = useSessionStorage<string | null>(
|
|
316
|
+
const [token, setToken, clearToken] = useSessionStorage<string | null>(
|
|
317
|
+
"auth-token",
|
|
318
|
+
null
|
|
319
|
+
);
|
|
297
320
|
|
|
298
321
|
const login = async (credentials: Credentials) => {
|
|
299
322
|
const response = await authenticate(credentials);
|
|
@@ -321,17 +344,13 @@ function ProtectedPage() {
|
|
|
321
344
|
### Custom Serialization (Date)
|
|
322
345
|
|
|
323
346
|
```tsx
|
|
324
|
-
import { useSessionStorage } from
|
|
347
|
+
import { useSessionStorage } from "@usefy/use-session-storage";
|
|
325
348
|
|
|
326
349
|
function SessionTimer() {
|
|
327
|
-
const [sessionStart] = useSessionStorage<Date>(
|
|
328
|
-
|
|
329
|
-
new Date(),
|
|
330
|
-
|
|
331
|
-
serializer: (date) => date.toISOString(),
|
|
332
|
-
deserializer: (str) => new Date(str),
|
|
333
|
-
}
|
|
334
|
-
);
|
|
350
|
+
const [sessionStart] = useSessionStorage<Date>("session-start", new Date(), {
|
|
351
|
+
serializer: (date) => date.toISOString(),
|
|
352
|
+
deserializer: (str) => new Date(str),
|
|
353
|
+
});
|
|
335
354
|
|
|
336
355
|
const [elapsed, setElapsed] = useState(0);
|
|
337
356
|
|
|
@@ -349,15 +368,19 @@ function SessionTimer() {
|
|
|
349
368
|
### Error Handling
|
|
350
369
|
|
|
351
370
|
```tsx
|
|
352
|
-
import { useSessionStorage } from
|
|
371
|
+
import { useSessionStorage } from "@usefy/use-session-storage";
|
|
353
372
|
|
|
354
373
|
function RobustSessionStorage() {
|
|
355
|
-
const [data, setData] = useSessionStorage(
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
374
|
+
const [data, setData] = useSessionStorage(
|
|
375
|
+
"session-data",
|
|
376
|
+
{ items: [] },
|
|
377
|
+
{
|
|
378
|
+
onError: (error) => {
|
|
379
|
+
console.error("Session storage error:", error.message);
|
|
380
|
+
toast.error("Failed to save session data");
|
|
381
|
+
},
|
|
382
|
+
}
|
|
383
|
+
);
|
|
361
384
|
|
|
362
385
|
return <DataEditor data={data} onChange={setData} />;
|
|
363
386
|
}
|
|
@@ -366,12 +389,12 @@ function RobustSessionStorage() {
|
|
|
366
389
|
### Lazy Initialization
|
|
367
390
|
|
|
368
391
|
```tsx
|
|
369
|
-
import { useSessionStorage } from
|
|
392
|
+
import { useSessionStorage } from "@usefy/use-session-storage";
|
|
370
393
|
|
|
371
394
|
function ExpensiveDefaultDemo() {
|
|
372
395
|
// Expensive computation only runs if no stored value exists
|
|
373
|
-
const [cache, setCache] = useSessionStorage(
|
|
374
|
-
console.log(
|
|
396
|
+
const [cache, setCache] = useSessionStorage("session-cache", () => {
|
|
397
|
+
console.log("Building initial cache...");
|
|
375
398
|
return buildExpensiveCache();
|
|
376
399
|
});
|
|
377
400
|
|
|
@@ -382,7 +405,7 @@ function ExpensiveDefaultDemo() {
|
|
|
382
405
|
### Quiz Progress
|
|
383
406
|
|
|
384
407
|
```tsx
|
|
385
|
-
import { useSessionStorage } from
|
|
408
|
+
import { useSessionStorage } from "@usefy/use-session-storage";
|
|
386
409
|
|
|
387
410
|
interface QuizState {
|
|
388
411
|
currentQuestion: number;
|
|
@@ -391,11 +414,14 @@ interface QuizState {
|
|
|
391
414
|
}
|
|
392
415
|
|
|
393
416
|
function Quiz() {
|
|
394
|
-
const [quiz, setQuiz, resetQuiz] = useSessionStorage<QuizState>(
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
417
|
+
const [quiz, setQuiz, resetQuiz] = useSessionStorage<QuizState>(
|
|
418
|
+
"quiz-progress",
|
|
419
|
+
{
|
|
420
|
+
currentQuestion: 0,
|
|
421
|
+
answers: {},
|
|
422
|
+
startTime: Date.now(),
|
|
423
|
+
}
|
|
424
|
+
);
|
|
399
425
|
|
|
400
426
|
const submitAnswer = (answer: string) => {
|
|
401
427
|
setQuiz((prev) => ({
|
|
@@ -435,21 +461,21 @@ import {
|
|
|
435
461
|
type UseSessionStorageOptions,
|
|
436
462
|
type UseSessionStorageReturn,
|
|
437
463
|
type InitialValue,
|
|
438
|
-
} from
|
|
464
|
+
} from "@usefy/use-session-storage";
|
|
439
465
|
|
|
440
466
|
// Generic type inference
|
|
441
|
-
const [name, setName] = useSessionStorage(
|
|
442
|
-
const [step, setStep] = useSessionStorage(
|
|
443
|
-
const [items, setItems] = useSessionStorage(
|
|
467
|
+
const [name, setName] = useSessionStorage("name", "Guest"); // string
|
|
468
|
+
const [step, setStep] = useSessionStorage("step", 1); // number
|
|
469
|
+
const [items, setItems] = useSessionStorage("items", ["a"]); // string[]
|
|
444
470
|
|
|
445
471
|
// Explicit generic type
|
|
446
472
|
interface FormData {
|
|
447
473
|
email: string;
|
|
448
474
|
message: string;
|
|
449
475
|
}
|
|
450
|
-
const [form, setForm] = useSessionStorage<FormData>(
|
|
451
|
-
email:
|
|
452
|
-
message:
|
|
476
|
+
const [form, setForm] = useSessionStorage<FormData>("form", {
|
|
477
|
+
email: "",
|
|
478
|
+
message: "",
|
|
453
479
|
});
|
|
454
480
|
```
|
|
455
481
|
|
|
@@ -461,12 +487,12 @@ This package maintains comprehensive test coverage to ensure reliability and sta
|
|
|
461
487
|
|
|
462
488
|
### Test Coverage
|
|
463
489
|
|
|
464
|
-
| Category
|
|
465
|
-
|
|
490
|
+
| Category | Coverage |
|
|
491
|
+
| ---------- | -------------- |
|
|
466
492
|
| Statements | 93.75% (45/48) |
|
|
467
|
-
| Branches
|
|
468
|
-
| Functions
|
|
469
|
-
| Lines
|
|
493
|
+
| Branches | 78.94% (15/19) |
|
|
494
|
+
| Functions | 100% (6/6) |
|
|
495
|
+
| Lines | 93.75% (45/48) |
|
|
470
496
|
|
|
471
497
|
### Test Categories
|
|
472
498
|
|
|
@@ -512,14 +538,14 @@ pnpm test --coverage
|
|
|
512
538
|
|
|
513
539
|
Explore other hooks in the **@usefy** collection:
|
|
514
540
|
|
|
515
|
-
| Package
|
|
516
|
-
|
|
517
|
-
| [@usefy/use-local-storage](https://www.npmjs.com/package/@usefy/use-local-storage)
|
|
518
|
-
| [@usefy/use-toggle](https://www.npmjs.com/package/@usefy/use-toggle)
|
|
519
|
-
| [@usefy/use-counter](https://www.npmjs.com/package/@usefy/use-counter)
|
|
520
|
-
| [@usefy/use-debounce](https://www.npmjs.com/package/@usefy/use-debounce)
|
|
521
|
-
| [@usefy/use-throttle](https://www.npmjs.com/package/@usefy/use-throttle)
|
|
522
|
-
| [@usefy/use-click-any-where](https://www.npmjs.com/package/@usefy/use-click-any-where) | Global click detection
|
|
541
|
+
| Package | Description |
|
|
542
|
+
| -------------------------------------------------------------------------------------- | ------------------------ |
|
|
543
|
+
| [@usefy/use-local-storage](https://www.npmjs.com/package/@usefy/use-local-storage) | Persistent localStorage |
|
|
544
|
+
| [@usefy/use-toggle](https://www.npmjs.com/package/@usefy/use-toggle) | Boolean state management |
|
|
545
|
+
| [@usefy/use-counter](https://www.npmjs.com/package/@usefy/use-counter) | Counter state management |
|
|
546
|
+
| [@usefy/use-debounce](https://www.npmjs.com/package/@usefy/use-debounce) | Value debouncing |
|
|
547
|
+
| [@usefy/use-throttle](https://www.npmjs.com/package/@usefy/use-throttle) | Value throttling |
|
|
548
|
+
| [@usefy/use-click-any-where](https://www.npmjs.com/package/@usefy/use-click-any-where) | Global click detection |
|
|
523
549
|
|
|
524
550
|
---
|
|
525
551
|
|