@usefy/use-local-storage 0.0.8 → 0.0.10
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 +540 -0
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/geon0529/usefy/master/assets/logo.png" alt="usefy logo" width="120" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">@usefy/use-local-storage</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>A powerful React hook for persisting state in localStorage with automatic cross-tab synchronization</strong>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/@usefy/use-local-storage">
|
|
13
|
+
<img src="https://img.shields.io/npm/v/@usefy/use-local-storage.svg?style=flat-square&color=007acc" alt="npm version" />
|
|
14
|
+
</a>
|
|
15
|
+
<a href="https://www.npmjs.com/package/@usefy/use-local-storage">
|
|
16
|
+
<img src="https://img.shields.io/npm/dm/@usefy/use-local-storage.svg?style=flat-square&color=007acc" alt="npm downloads" />
|
|
17
|
+
</a>
|
|
18
|
+
<a href="https://bundlephobia.com/package/@usefy/use-local-storage">
|
|
19
|
+
<img src="https://img.shields.io/bundlephobia/minzip/@usefy/use-local-storage?style=flat-square&color=007acc" alt="bundle size" />
|
|
20
|
+
</a>
|
|
21
|
+
<a href="https://github.com/geon0529/usefy/blob/master/LICENSE">
|
|
22
|
+
<img src="https://img.shields.io/npm/l/@usefy/use-local-storage.svg?style=flat-square&color=007acc" alt="license" />
|
|
23
|
+
</a>
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
<a href="#installation">Installation</a> •
|
|
28
|
+
<a href="#quick-start">Quick Start</a> •
|
|
29
|
+
<a href="#api-reference">API Reference</a> •
|
|
30
|
+
<a href="#examples">Examples</a> •
|
|
31
|
+
<a href="#license">License</a>
|
|
32
|
+
</p>
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Overview
|
|
37
|
+
|
|
38
|
+
`@usefy/use-local-storage` provides a `useState`-like API for persisting data in localStorage. Features include cross-tab synchronization, custom serialization, lazy initialization, error handling, and a `removeValue` function. Data persists across browser sessions.
|
|
39
|
+
|
|
40
|
+
**Part of the [@usefy](https://www.npmjs.com/org/usefy) ecosystem** — a collection of production-ready React hooks designed for modern applications.
|
|
41
|
+
|
|
42
|
+
### Why use-local-storage?
|
|
43
|
+
|
|
44
|
+
- **Zero Dependencies** — Pure React implementation with no external dependencies
|
|
45
|
+
- **TypeScript First** — Full type safety with generics and exported interfaces
|
|
46
|
+
- **useState-like API** — Familiar tuple return: `[value, setValue, removeValue]`
|
|
47
|
+
- **Cross-Tab Sync** — Automatic synchronization across browser tabs
|
|
48
|
+
- **Custom Serialization** — Support for Date, Map, Set, or any custom type
|
|
49
|
+
- **Lazy Initialization** — Function initializer support for expensive defaults
|
|
50
|
+
- **Error Handling** — `onError` callback for graceful error recovery
|
|
51
|
+
- **SSR Compatible** — Works seamlessly with Next.js, Remix, and other SSR frameworks
|
|
52
|
+
- **Stable References** — Memoized functions for optimal performance
|
|
53
|
+
- **Well Tested** — Comprehensive test coverage with Vitest
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# npm
|
|
61
|
+
npm install @usefy/use-local-storage
|
|
62
|
+
|
|
63
|
+
# yarn
|
|
64
|
+
yarn add @usefy/use-local-storage
|
|
65
|
+
|
|
66
|
+
# pnpm
|
|
67
|
+
pnpm add @usefy/use-local-storage
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Peer Dependencies
|
|
71
|
+
|
|
72
|
+
This package requires React 18 or 19:
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"peerDependencies": {
|
|
77
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Quick Start
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { useLocalStorage } from '@usefy/use-local-storage';
|
|
88
|
+
|
|
89
|
+
function ThemeToggle() {
|
|
90
|
+
const [theme, setTheme, removeTheme] = useLocalStorage('theme', 'light');
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div>
|
|
94
|
+
<p>Current theme: {theme}</p>
|
|
95
|
+
<button onClick={() => setTheme('dark')}>Dark</button>
|
|
96
|
+
<button onClick={() => setTheme('light')}>Light</button>
|
|
97
|
+
<button onClick={removeTheme}>Reset</button>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## API Reference
|
|
106
|
+
|
|
107
|
+
### `useLocalStorage<T>(key, initialValue, options?)`
|
|
108
|
+
|
|
109
|
+
A hook that persists state in localStorage with automatic synchronization.
|
|
110
|
+
|
|
111
|
+
#### Parameters
|
|
112
|
+
|
|
113
|
+
| Parameter | Type | Description |
|
|
114
|
+
|-----------|------|-------------|
|
|
115
|
+
| `key` | `string` | The localStorage key |
|
|
116
|
+
| `initialValue` | `T \| () => T` | Initial value or lazy initializer function |
|
|
117
|
+
| `options` | `UseLocalStorageOptions<T>` | Configuration options |
|
|
118
|
+
|
|
119
|
+
#### Options
|
|
120
|
+
|
|
121
|
+
| Option | Type | Default | Description |
|
|
122
|
+
|--------|------|---------|-------------|
|
|
123
|
+
| `serializer` | `(value: T) => string` | `JSON.stringify` | Custom serializer function |
|
|
124
|
+
| `deserializer` | `(value: string) => T` | `JSON.parse` | Custom deserializer function |
|
|
125
|
+
| `syncTabs` | `boolean` | `true` | Sync value across browser tabs |
|
|
126
|
+
| `onError` | `(error: Error) => void` | — | Callback for error handling |
|
|
127
|
+
|
|
128
|
+
#### Returns `[T, SetValue<T>, RemoveValue]`
|
|
129
|
+
|
|
130
|
+
| Index | Type | Description |
|
|
131
|
+
|-------|------|-------------|
|
|
132
|
+
| `[0]` | `T` | Current stored value |
|
|
133
|
+
| `[1]` | `Dispatch<SetStateAction<T>>` | Function to update value (same as useState) |
|
|
134
|
+
| `[2]` | `() => void` | Function to remove value and reset to initial |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Examples
|
|
139
|
+
|
|
140
|
+
### Basic String Storage
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import { useLocalStorage } from '@usefy/use-local-storage';
|
|
144
|
+
|
|
145
|
+
function NameInput() {
|
|
146
|
+
const [name, setName, removeName] = useLocalStorage('userName', '');
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<div>
|
|
150
|
+
<input
|
|
151
|
+
value={name}
|
|
152
|
+
onChange={(e) => setName(e.target.value)}
|
|
153
|
+
placeholder="Enter your name"
|
|
154
|
+
/>
|
|
155
|
+
<button onClick={removeName}>Clear</button>
|
|
156
|
+
<p>Hello, {name || 'Guest'}!</p>
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Object State
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
import { useLocalStorage } from '@usefy/use-local-storage';
|
|
166
|
+
|
|
167
|
+
interface UserSettings {
|
|
168
|
+
notifications: boolean;
|
|
169
|
+
language: string;
|
|
170
|
+
fontSize: number;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function SettingsPanel() {
|
|
174
|
+
const [settings, setSettings] = useLocalStorage<UserSettings>('settings', {
|
|
175
|
+
notifications: true,
|
|
176
|
+
language: 'en',
|
|
177
|
+
fontSize: 16,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<div>
|
|
182
|
+
<label>
|
|
183
|
+
<input
|
|
184
|
+
type="checkbox"
|
|
185
|
+
checked={settings.notifications}
|
|
186
|
+
onChange={(e) =>
|
|
187
|
+
setSettings((prev) => ({ ...prev, notifications: e.target.checked }))
|
|
188
|
+
}
|
|
189
|
+
/>
|
|
190
|
+
Enable Notifications
|
|
191
|
+
</label>
|
|
192
|
+
|
|
193
|
+
<select
|
|
194
|
+
value={settings.language}
|
|
195
|
+
onChange={(e) =>
|
|
196
|
+
setSettings((prev) => ({ ...prev, language: e.target.value }))
|
|
197
|
+
}
|
|
198
|
+
>
|
|
199
|
+
<option value="en">English</option>
|
|
200
|
+
<option value="ko">한국어</option>
|
|
201
|
+
<option value="ja">日本語</option>
|
|
202
|
+
</select>
|
|
203
|
+
|
|
204
|
+
<input
|
|
205
|
+
type="range"
|
|
206
|
+
min="12"
|
|
207
|
+
max="24"
|
|
208
|
+
value={settings.fontSize}
|
|
209
|
+
onChange={(e) =>
|
|
210
|
+
setSettings((prev) => ({ ...prev, fontSize: +e.target.value }))
|
|
211
|
+
}
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Lazy Initialization
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
import { useLocalStorage } from '@usefy/use-local-storage';
|
|
222
|
+
|
|
223
|
+
function ExpensiveDefaultDemo() {
|
|
224
|
+
// Expensive computation only runs if no stored value exists
|
|
225
|
+
const [config, setConfig] = useLocalStorage('appConfig', () => {
|
|
226
|
+
console.log('Computing expensive default...');
|
|
227
|
+
return generateComplexDefaultConfig();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
return <ConfigEditor config={config} onChange={setConfig} />;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Custom Serialization (Date)
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
import { useLocalStorage } from '@usefy/use-local-storage';
|
|
238
|
+
|
|
239
|
+
function DatePicker() {
|
|
240
|
+
const [lastVisit, setLastVisit] = useLocalStorage<Date>(
|
|
241
|
+
'lastVisit',
|
|
242
|
+
new Date(),
|
|
243
|
+
{
|
|
244
|
+
serializer: (date) => date.toISOString(),
|
|
245
|
+
deserializer: (str) => new Date(str),
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<div>
|
|
251
|
+
<p>Last visit: {lastVisit.toLocaleDateString()}</p>
|
|
252
|
+
<button onClick={() => setLastVisit(new Date())}>Update</button>
|
|
253
|
+
</div>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Custom Serialization (Map)
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
import { useLocalStorage } from '@usefy/use-local-storage';
|
|
262
|
+
|
|
263
|
+
function FavoritesManager() {
|
|
264
|
+
const [favorites, setFavorites] = useLocalStorage<Map<string, boolean>>(
|
|
265
|
+
'favorites',
|
|
266
|
+
new Map(),
|
|
267
|
+
{
|
|
268
|
+
serializer: (map) => JSON.stringify([...map.entries()]),
|
|
269
|
+
deserializer: (str) => new Map(JSON.parse(str)),
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
const toggleFavorite = (id: string) => {
|
|
274
|
+
setFavorites((prev) => {
|
|
275
|
+
const next = new Map(prev);
|
|
276
|
+
next.has(id) ? next.delete(id) : next.set(id, true);
|
|
277
|
+
return next;
|
|
278
|
+
});
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
return (
|
|
282
|
+
<ul>
|
|
283
|
+
{items.map((item) => (
|
|
284
|
+
<li key={item.id}>
|
|
285
|
+
{item.name}
|
|
286
|
+
<button onClick={() => toggleFavorite(item.id)}>
|
|
287
|
+
{favorites.has(item.id) ? '★' : '☆'}
|
|
288
|
+
</button>
|
|
289
|
+
</li>
|
|
290
|
+
))}
|
|
291
|
+
</ul>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Error Handling
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
import { useLocalStorage } from '@usefy/use-local-storage';
|
|
300
|
+
|
|
301
|
+
function RobustStorage() {
|
|
302
|
+
const [data, setData] = useLocalStorage('userData', { items: [] }, {
|
|
303
|
+
onError: (error) => {
|
|
304
|
+
console.error('Storage error:', error.message);
|
|
305
|
+
// Could show toast notification, log to analytics, etc.
|
|
306
|
+
toast.error('Failed to save data. Storage may be full.');
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
return <DataEditor data={data} onChange={setData} />;
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Cross-Tab Synchronization
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
import { useLocalStorage } from '@usefy/use-local-storage';
|
|
318
|
+
|
|
319
|
+
function CartCounter() {
|
|
320
|
+
// Changes in one tab automatically sync to other tabs
|
|
321
|
+
const [cartItems, setCartItems] = useLocalStorage<string[]>('cart', []);
|
|
322
|
+
|
|
323
|
+
const addItem = (item: string) => {
|
|
324
|
+
setCartItems((prev) => [...prev, item]);
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
return (
|
|
328
|
+
<div>
|
|
329
|
+
<span>Cart: {cartItems.length} items</span>
|
|
330
|
+
{/* Other tabs will see the updated count */}
|
|
331
|
+
</div>
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Disable Tab Sync
|
|
337
|
+
|
|
338
|
+
```tsx
|
|
339
|
+
import { useLocalStorage } from '@usefy/use-local-storage';
|
|
340
|
+
|
|
341
|
+
function LocalOnlyData() {
|
|
342
|
+
// Don't sync changes from other tabs
|
|
343
|
+
const [draft, setDraft] = useLocalStorage('draft', '', {
|
|
344
|
+
syncTabs: false,
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
return (
|
|
348
|
+
<textarea
|
|
349
|
+
value={draft}
|
|
350
|
+
onChange={(e) => setDraft(e.target.value)}
|
|
351
|
+
placeholder="This draft won't sync with other tabs"
|
|
352
|
+
/>
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Shopping Cart
|
|
358
|
+
|
|
359
|
+
```tsx
|
|
360
|
+
import { useLocalStorage } from '@usefy/use-local-storage';
|
|
361
|
+
|
|
362
|
+
interface CartItem {
|
|
363
|
+
id: string;
|
|
364
|
+
name: string;
|
|
365
|
+
quantity: number;
|
|
366
|
+
price: number;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function ShoppingCart() {
|
|
370
|
+
const [cart, setCart, clearCart] = useLocalStorage<CartItem[]>('cart', []);
|
|
371
|
+
|
|
372
|
+
const addToCart = (product: Product) => {
|
|
373
|
+
setCart((prev) => {
|
|
374
|
+
const existing = prev.find((item) => item.id === product.id);
|
|
375
|
+
if (existing) {
|
|
376
|
+
return prev.map((item) =>
|
|
377
|
+
item.id === product.id
|
|
378
|
+
? { ...item, quantity: item.quantity + 1 }
|
|
379
|
+
: item
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
return [...prev, { ...product, quantity: 1 }];
|
|
383
|
+
});
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
const total = cart.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
|
387
|
+
|
|
388
|
+
return (
|
|
389
|
+
<div>
|
|
390
|
+
<h2>Cart ({cart.length} items)</h2>
|
|
391
|
+
<ul>
|
|
392
|
+
{cart.map((item) => (
|
|
393
|
+
<li key={item.id}>
|
|
394
|
+
{item.name} x {item.quantity} - ${item.price * item.quantity}
|
|
395
|
+
</li>
|
|
396
|
+
))}
|
|
397
|
+
</ul>
|
|
398
|
+
<p>Total: ${total.toFixed(2)}</p>
|
|
399
|
+
<button onClick={clearCart}>Clear Cart</button>
|
|
400
|
+
</div>
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## TypeScript
|
|
408
|
+
|
|
409
|
+
This hook is written in TypeScript with full generic support.
|
|
410
|
+
|
|
411
|
+
```tsx
|
|
412
|
+
import {
|
|
413
|
+
useLocalStorage,
|
|
414
|
+
type UseLocalStorageOptions,
|
|
415
|
+
type UseLocalStorageReturn,
|
|
416
|
+
type InitialValue,
|
|
417
|
+
} from '@usefy/use-local-storage';
|
|
418
|
+
|
|
419
|
+
// Generic type inference
|
|
420
|
+
const [name, setName] = useLocalStorage('name', 'John'); // string
|
|
421
|
+
const [count, setCount] = useLocalStorage('count', 0); // number
|
|
422
|
+
const [items, setItems] = useLocalStorage('items', [1, 2]); // number[]
|
|
423
|
+
|
|
424
|
+
// Explicit generic type
|
|
425
|
+
interface User {
|
|
426
|
+
id: string;
|
|
427
|
+
name: string;
|
|
428
|
+
}
|
|
429
|
+
const [user, setUser] = useLocalStorage<User | null>('user', null);
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## Testing
|
|
435
|
+
|
|
436
|
+
This package maintains comprehensive test coverage to ensure reliability and stability.
|
|
437
|
+
|
|
438
|
+
### Test Coverage
|
|
439
|
+
|
|
440
|
+
| Category | Tests | Coverage |
|
|
441
|
+
|----------|-------|----------|
|
|
442
|
+
| Initialization | 7 | 100% |
|
|
443
|
+
| setValue | 5 | 100% |
|
|
444
|
+
| removeValue | 2 | 100% |
|
|
445
|
+
| Type Preservation | 5 | 100% |
|
|
446
|
+
| Custom Serialization | 2 | 100% |
|
|
447
|
+
| Cross-Tab Sync | 5 | 100% |
|
|
448
|
+
| Key Changes | 2 | 100% |
|
|
449
|
+
| Function Stability | 3 | 100% |
|
|
450
|
+
| Multiple Instances | 2 | 100% |
|
|
451
|
+
| Edge Cases | 6 | 100% |
|
|
452
|
+
| Cleanup | 1 | 100% |
|
|
453
|
+
| **Total** | **40** | **95.08%** |
|
|
454
|
+
|
|
455
|
+
### Test Categories
|
|
456
|
+
|
|
457
|
+
<details>
|
|
458
|
+
<summary><strong>Initialization Tests</strong></summary>
|
|
459
|
+
|
|
460
|
+
- Return initial value when localStorage is empty
|
|
461
|
+
- Return stored value when localStorage has data
|
|
462
|
+
- Support lazy initialization with function
|
|
463
|
+
- Not call initializer when localStorage has data
|
|
464
|
+
- Fallback to initial value when JSON parse fails
|
|
465
|
+
- Call onError when localStorage read fails
|
|
466
|
+
|
|
467
|
+
</details>
|
|
468
|
+
|
|
469
|
+
<details>
|
|
470
|
+
<summary><strong>Cross-Tab Sync Tests</strong></summary>
|
|
471
|
+
|
|
472
|
+
- Update value when storage event is fired
|
|
473
|
+
- Reset to initial when key is removed in another tab
|
|
474
|
+
- Ignore storage events for different keys
|
|
475
|
+
- Not sync when syncTabs is false
|
|
476
|
+
- Fallback to initial value on invalid JSON
|
|
477
|
+
|
|
478
|
+
</details>
|
|
479
|
+
|
|
480
|
+
### Running Tests
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
# Run all tests
|
|
484
|
+
pnpm test
|
|
485
|
+
|
|
486
|
+
# Run tests in watch mode
|
|
487
|
+
pnpm test:watch
|
|
488
|
+
|
|
489
|
+
# Run tests with coverage report
|
|
490
|
+
pnpm test --coverage
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## Related Packages
|
|
496
|
+
|
|
497
|
+
Explore other hooks in the **@usefy** collection:
|
|
498
|
+
|
|
499
|
+
| Package | Description |
|
|
500
|
+
|---------|-------------|
|
|
501
|
+
| [@usefy/use-session-storage](https://www.npmjs.com/package/@usefy/use-session-storage) | Session storage persistence |
|
|
502
|
+
| [@usefy/use-toggle](https://www.npmjs.com/package/@usefy/use-toggle) | Boolean state management |
|
|
503
|
+
| [@usefy/use-counter](https://www.npmjs.com/package/@usefy/use-counter) | Counter state management |
|
|
504
|
+
| [@usefy/use-debounce](https://www.npmjs.com/package/@usefy/use-debounce) | Value debouncing |
|
|
505
|
+
| [@usefy/use-throttle](https://www.npmjs.com/package/@usefy/use-throttle) | Value throttling |
|
|
506
|
+
| [@usefy/use-click-any-where](https://www.npmjs.com/package/@usefy/use-click-any-where) | Global click detection |
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
## Contributing
|
|
511
|
+
|
|
512
|
+
We welcome contributions! Please see our [Contributing Guide](https://github.com/geon0529/usefy/blob/master/CONTRIBUTING.md) for details.
|
|
513
|
+
|
|
514
|
+
```bash
|
|
515
|
+
# Clone the repository
|
|
516
|
+
git clone https://github.com/geon0529/usefy.git
|
|
517
|
+
|
|
518
|
+
# Install dependencies
|
|
519
|
+
pnpm install
|
|
520
|
+
|
|
521
|
+
# Run tests
|
|
522
|
+
pnpm test
|
|
523
|
+
|
|
524
|
+
# Build
|
|
525
|
+
pnpm build
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
530
|
+
## License
|
|
531
|
+
|
|
532
|
+
MIT © [mirunamu](https://github.com/geon0529)
|
|
533
|
+
|
|
534
|
+
This package is part of the [usefy](https://github.com/geon0529/usefy) monorepo.
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
<p align="center">
|
|
539
|
+
<sub>Built with care by the usefy team</sub>
|
|
540
|
+
</p>
|
package/package.json
CHANGED