@malconlobo/react-utility-hooks 1.0.0 → 1.0.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 +183 -23
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,49 +1,104 @@
|
|
|
1
|
-
# react-utility-hooks
|
|
1
|
+
# @malconlobo/react-utility-hooks
|
|
2
2
|
|
|
3
|
-
A collection of robust, type-safe utility React hooks.
|
|
3
|
+
A collection of robust, type-safe, and thoroughly tested utility React hooks.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install react-utility-hooks
|
|
8
|
+
npm install @malconlobo/react-utility-hooks
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
## Hooks
|
|
11
|
+
## Hooks
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- `usePrevious`: Tracks the previous state or prop value.
|
|
16
|
-
- `useLocalStorage`: Syncs state with local storage.
|
|
17
|
-
- `useMediaQuery`: Tracks the state of a CSS media query.
|
|
18
|
-
- `useAsync`: Handles asynchronous operations and exposes status/error.
|
|
19
|
-
- `useIntersectionObserver`: Tracks element visibility in the viewport.
|
|
20
|
-
- `useInfiniteScroll`: Triggers callbacks when scrolling near the bottom of a container.
|
|
13
|
+
### `useDebounce`
|
|
14
|
+
Delays updating a value until after a specified delay has elapsed since the last time the value was changed.
|
|
21
15
|
|
|
22
|
-
|
|
16
|
+
**Parameters**
|
|
17
|
+
- `value: T` - The value to debounce.
|
|
18
|
+
- `delay: number` - The delay in milliseconds.
|
|
23
19
|
|
|
24
|
-
|
|
20
|
+
**Example**
|
|
25
21
|
```tsx
|
|
26
|
-
import { useState } from 'react';
|
|
27
|
-
import { useDebounce } from 'react-utility-hooks';
|
|
22
|
+
import { useState, useEffect } from 'react';
|
|
23
|
+
import { useDebounce } from '@malconlobo/react-utility-hooks';
|
|
28
24
|
|
|
29
|
-
function
|
|
25
|
+
function SearchInput() {
|
|
30
26
|
const [searchTerm, setSearchTerm] = useState('');
|
|
31
27
|
const debouncedSearchTerm = useDebounce(searchTerm, 500);
|
|
32
28
|
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (debouncedSearchTerm) {
|
|
31
|
+
// Fetch results
|
|
32
|
+
console.log('Fetching for:', debouncedSearchTerm);
|
|
33
|
+
}
|
|
34
|
+
}, [debouncedSearchTerm]);
|
|
35
|
+
|
|
36
|
+
return <input value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />;
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### `useThrottle`
|
|
41
|
+
Returns a throttled version of a value that only updates at most once every specified amount of milliseconds.
|
|
42
|
+
|
|
43
|
+
**Parameters**
|
|
44
|
+
- `value: T` - The value to throttle.
|
|
45
|
+
- `delay: number` - The throttle limit in milliseconds.
|
|
46
|
+
|
|
47
|
+
**Example**
|
|
48
|
+
```tsx
|
|
49
|
+
import { useState } from 'react';
|
|
50
|
+
import { useThrottle } from '@malconlobo/react-utility-hooks';
|
|
51
|
+
|
|
52
|
+
function ThrottledInput() {
|
|
53
|
+
const [value, setValue] = useState('');
|
|
54
|
+
const throttledValue = useThrottle(value, 1000);
|
|
55
|
+
|
|
33
56
|
return (
|
|
34
|
-
<
|
|
35
|
-
value={
|
|
36
|
-
|
|
37
|
-
|
|
57
|
+
<div>
|
|
58
|
+
<input value={value} onChange={(e) => setValue(e.target.value)} />
|
|
59
|
+
<p>Throttled value: {throttledValue}</p>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### `usePrevious`
|
|
66
|
+
Stores and returns the previous state or prop value.
|
|
67
|
+
|
|
68
|
+
**Parameters**
|
|
69
|
+
- `value: T` - The value to track.
|
|
70
|
+
|
|
71
|
+
**Example**
|
|
72
|
+
```tsx
|
|
73
|
+
import { useState } from 'react';
|
|
74
|
+
import { usePrevious } from '@malconlobo/react-utility-hooks';
|
|
75
|
+
|
|
76
|
+
function Counter() {
|
|
77
|
+
const [count, setCount] = useState(0);
|
|
78
|
+
const previousCount = usePrevious(count);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div>
|
|
82
|
+
<p>Current: {count}</p>
|
|
83
|
+
<p>Previous: {previousCount}</p>
|
|
84
|
+
<button onClick={() => setCount(c => c + 1)}>Increment</button>
|
|
85
|
+
</div>
|
|
38
86
|
);
|
|
39
87
|
}
|
|
40
88
|
```
|
|
41
89
|
|
|
42
90
|
### `useLocalStorage`
|
|
91
|
+
Syncs state to local storage so that it persists through a page refresh.
|
|
92
|
+
|
|
93
|
+
**Parameters**
|
|
94
|
+
- `key: string` - The local storage key.
|
|
95
|
+
- `initialValue: T` - The initial value if no value exists in local storage.
|
|
96
|
+
|
|
97
|
+
**Example**
|
|
43
98
|
```tsx
|
|
44
|
-
import { useLocalStorage } from 'react-utility-hooks';
|
|
99
|
+
import { useLocalStorage } from '@malconlobo/react-utility-hooks';
|
|
45
100
|
|
|
46
|
-
function
|
|
101
|
+
function ThemeSelector() {
|
|
47
102
|
const [theme, setTheme] = useLocalStorage('theme', 'light');
|
|
48
103
|
|
|
49
104
|
return (
|
|
@@ -54,5 +109,110 @@ function App() {
|
|
|
54
109
|
}
|
|
55
110
|
```
|
|
56
111
|
|
|
112
|
+
### `useMediaQuery`
|
|
113
|
+
Tracks the state of a CSS media query.
|
|
114
|
+
|
|
115
|
+
**Parameters**
|
|
116
|
+
- `query: string` - The media query to track (e.g., `(min-width: 768px)`).
|
|
117
|
+
|
|
118
|
+
**Example**
|
|
119
|
+
```tsx
|
|
120
|
+
import { useMediaQuery } from '@malconlobo/react-utility-hooks';
|
|
121
|
+
|
|
122
|
+
function ResponsiveComponent() {
|
|
123
|
+
const isDesktop = useMediaQuery('(min-width: 768px)');
|
|
124
|
+
|
|
125
|
+
return <div>{isDesktop ? 'Desktop View' : 'Mobile View'}</div>;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### `useAsync`
|
|
130
|
+
Handles asynchronous operations (promises) providing `status`, `value`, and `error` state.
|
|
131
|
+
|
|
132
|
+
**Parameters**
|
|
133
|
+
- `asyncFunction: () => Promise<T>` - The async function to execute.
|
|
134
|
+
- `immediate?: boolean` - (Optional, default `true`) Whether to execute the function immediately on mount.
|
|
135
|
+
|
|
136
|
+
**Example**
|
|
137
|
+
```tsx
|
|
138
|
+
import { useAsync } from '@malconlobo/react-utility-hooks';
|
|
139
|
+
|
|
140
|
+
const fetchUser = () => fetch('/api/user').then(res => res.json());
|
|
141
|
+
|
|
142
|
+
function UserProfile() {
|
|
143
|
+
const { status, value, error, execute } = useAsync(fetchUser);
|
|
144
|
+
|
|
145
|
+
if (status === 'pending' || status === 'idle') return <p>Loading...</p>;
|
|
146
|
+
if (status === 'error') return <p>Error: {error?.message}</p>;
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<div>
|
|
150
|
+
<h1>{value?.name}</h1>
|
|
151
|
+
<button onClick={execute}>Refresh</button>
|
|
152
|
+
</div>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### `useIntersectionObserver`
|
|
158
|
+
Tracks the intersection of a target element with the viewport.
|
|
159
|
+
|
|
160
|
+
**Parameters**
|
|
161
|
+
- `elementRef: RefObject<Element>` - The ref of the element to observe.
|
|
162
|
+
- `options: IntersectionObserverInit & { freezeOnceVisible?: boolean }` - Configuration options:
|
|
163
|
+
- `threshold?: number | number[]` - Ratio of intersection (0 to 1).
|
|
164
|
+
- `root?: Element | null` - Element used as the viewport.
|
|
165
|
+
- `rootMargin?: string` - Margin around the root.
|
|
166
|
+
- `freezeOnceVisible?: boolean` - Stop tracking once the element becomes visible.
|
|
167
|
+
|
|
168
|
+
**Example**
|
|
169
|
+
```tsx
|
|
170
|
+
import { useRef } from 'react';
|
|
171
|
+
import { useIntersectionObserver } from '@malconlobo/react-utility-hooks';
|
|
172
|
+
|
|
173
|
+
function FadeInSection() {
|
|
174
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
175
|
+
const entry = useIntersectionObserver(ref, { freezeOnceVisible: true, threshold: 0.5 });
|
|
176
|
+
const isVisible = !!entry?.isIntersecting;
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<div ref={ref} style={{ opacity: isVisible ? 1 : 0, transition: 'opacity 1s' }}>
|
|
180
|
+
I will fade in when visible!
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### `useInfiniteScroll`
|
|
187
|
+
Triggers a callback when the user scrolls near the bottom of a container.
|
|
188
|
+
|
|
189
|
+
**Parameters**
|
|
190
|
+
- `callback: () => void` - The function to call when the element intersects.
|
|
191
|
+
- `options?: IntersectionObserverInit` - Options for the intersection observer (default: `{ rootMargin: '100px' }`).
|
|
192
|
+
|
|
193
|
+
**Example**
|
|
194
|
+
```tsx
|
|
195
|
+
import { useState } from 'react';
|
|
196
|
+
import { useInfiniteScroll } from '@malconlobo/react-utility-hooks';
|
|
197
|
+
|
|
198
|
+
function InfiniteList() {
|
|
199
|
+
const [items, setItems] = useState([1, 2, 3, 4, 5]);
|
|
200
|
+
|
|
201
|
+
const loadMore = () => {
|
|
202
|
+
// Simulate loading data
|
|
203
|
+
setItems(prev => [...prev, prev.length + 1, prev.length + 2]);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const loaderRef = useInfiniteScroll<HTMLDivElement>(loadMore);
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<div>
|
|
210
|
+
{items.map(item => <div key={item} style={{ height: 100 }}>Item {item}</div>)}
|
|
211
|
+
<div ref={loaderRef}>Loading more...</div>
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
57
217
|
## License
|
|
58
218
|
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malconlobo/react-utility-hooks",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A collection of utility React hooks.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
},
|
|
48
48
|
"homepage": "https://github.com/malconlobo/react-utility-hooks#readme",
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
|
51
|
-
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
|
50
|
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
51
|
+
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@testing-library/react": "^14.0.0",
|