Skip to content

Commit e60fa65

Browse files
authored
feat: add typing support for react 19 (#1627)
Change adapts to types changes of React19. Otherwise no changes done. Deprecated `MutableRefObject` remained to ensure backwards compatibility.
1 parent 3ddbe89 commit e60fa65

File tree

20 files changed

+47
-60
lines changed

20 files changed

+47
-60
lines changed

eslint.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ const config = [
1515
...mdConfig,
1616
...typescriptConfig,
1717
...vitestConfig,
18+
{
19+
files: ['**/*.ts'],
20+
rules: {
21+
'@typescript-eslint/no-deprecated': 'off',
22+
},
23+
},
1824
];
1925

2026
export default config;

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
},
5050
"peerDependencies": {
5151
"js-cookie": "^3.0.5",
52-
"react": "^16.8 || ^17 || ^18",
53-
"react-dom": "^16.8 || ^17 || ^18"
52+
"react": "^16.8 || ^17 || ^18 || ^19",
53+
"react-dom": "^16.8 || ^17 || ^18 || ^19"
5454
},
5555
"peerDependenciesMeta": {
5656
"js-cookie": {
@@ -64,8 +64,8 @@
6464
"@react-hookz/eslint-formatter-gha": "^3.0.4",
6565
"@testing-library/react-hooks": "^8.0.1",
6666
"@types/js-cookie": "^3.0.6",
67-
"@types/react": "^17.0.83",
68-
"@types/react-dom": "^17.0.26",
67+
"@types/react": "^19.0.10",
68+
"@types/react-dom": "^19.0.4",
6969
"@vitest/coverage-v8": "^3.0.8",
7070
"commitlint": "^19.7.1",
7171
"eslint": "^9.21.0",

src/useAsync/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ export function useAsync<Result, Args extends unknown[] = unknown[]>(
7272
error: undefined,
7373
result: initialValue,
7474
});
75-
const promiseRef = useRef<Promise<Result>>();
76-
const argsRef = useRef<Args>();
75+
const promiseRef = useRef<Promise<Result>>(undefined);
76+
const argsRef = useRef<Args>(undefined);
7777

7878
const methods = useSyncedRef({
7979
execute(...params: Args) {

src/useAsyncAbortable/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export function useAsyncAbortable<Result, Args extends unknown[] = unknown[]>(
5959
UseAsyncAbortableActions<Result, Args>,
6060
UseAsyncAbortableMeta<Result, Args>,
6161
] {
62-
const abortController = useRef<AbortController>();
62+
const abortController = useRef<AbortController>(undefined);
6363

6464
const fn = async (...args: Args): Promise<Result> => {
6565
// Abort previous async

src/useClickOutside/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const DEFAULT_EVENTS = ['mousedown', 'touchstart'];
1313
* 'mousedown', 'touchstart'
1414
*/
1515
export function useClickOutside<T extends HTMLElement>(
16-
ref: RefObject<T> | MutableRefObject<T>,
16+
ref: RefObject<T | null> | MutableRefObject<T | null>,
1717
callback: EventListener,
1818
events: string[] = DEFAULT_EVENTS,
1919
): void {

src/useCustomCompareEffect/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export function useCustomCompareEffect<
2929
effectHook: EffectHook<Callback, Deps, HookRestArgs> = useEffect,
3030
...effectHookRestArgs: R
3131
): void {
32-
const dependencies = useRef<Deps>();
32+
const dependencies = useRef<Deps>(undefined);
3333

3434
// Effects are not run during SSR, therefore, it makes no sense to invoke the comparator
3535
if (

src/useCustomCompareMemo/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const useCustomCompareMemo = <T, Deps extends DependencyList>(
1414
deps: Deps,
1515
comparator: DependenciesComparator<Deps>,
1616
): T => {
17-
const dependencies = useRef<Deps>();
17+
const dependencies = useRef<Deps>(undefined);
1818

1919
if (dependencies.current === undefined || !comparator(dependencies.current, deps)) {
2020
dependencies.current = deps;

src/useDebouncedCallback/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ export function useDebouncedCallback<Fn extends (...args: any[]) => any>(
2222
delay: number,
2323
maxWait = 0,
2424
): DebouncedFunction<Fn> {
25-
const timeout = useRef<ReturnType<typeof setTimeout>>();
26-
const waitTimeout = useRef<ReturnType<typeof setTimeout>>();
25+
const timeout = useRef<ReturnType<typeof setTimeout>>(undefined);
26+
const waitTimeout = useRef<ReturnType<typeof setTimeout>>(undefined);
2727
const cb = useRef(callback);
28-
const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>();
28+
const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>(undefined);
2929

3030
const clear = () => {
3131
if (timeout.current) {
@@ -83,6 +83,6 @@ export function useDebouncedCallback<Fn extends (...args: any[]) => any>(
8383
});
8484

8585
return wrapped;
86-
// eslint-disable-next-line react-hooks/exhaustive-deps,@typescript-eslint/no-unsafe-assignment
86+
// eslint-disable-next-line react-hooks/exhaustive-deps
8787
}, [delay, maxWait, ...deps]);
8888
}

src/useEventListener/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {hasOwnProperty, off, on} from '../util/misc.js';
1111
* something like `[eventName, listener, options]`.
1212
*/
1313
export function useEventListener<T extends EventTarget>(
14-
target: RefObject<T> | T | null,
14+
target: RefObject<T | null> | T | null,
1515
...params:
1616
| Parameters<T['addEventListener']>
1717
| [string, EventListenerOrEventListenerObject | ((...args: any[]) => any), ...any]
@@ -60,6 +60,6 @@ export function useEventListener<T extends EventTarget>(
6060
}, [target, params[0]]);
6161
}
6262

63-
function isRefObject<T>(target: RefObject<T> | T | null): target is RefObject<T> {
63+
function isRefObject<T>(target: RefObject<T | null> | T | null): target is RefObject<T | null> {
6464
return target !== null && typeof target === 'object' && hasOwnProperty(target, 'current');
6565
}

src/useHookableRef/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export function useHookableRef<T>(
88
onSet?: HookableRefHandler<T>,
99
onGet?: HookableRefHandler<T>
1010
): MutableRefObject<T>;
11-
export function useHookableRef<T = undefined>(): MutableRefObject<T | undefined>;
11+
export function useHookableRef<T = undefined>(): MutableRefObject<T | null | undefined>;
1212

1313
/**
1414
* Like `React.useRef` but it is possible to define get and set handlers.
@@ -23,7 +23,7 @@ export function useHookableRef<T>(
2323
initialValue?: T,
2424
onSet?: HookableRefHandler<T>,
2525
onGet?: HookableRefHandler<T>,
26-
): MutableRefObject<T | undefined> {
26+
): MutableRefObject<T | null | undefined> {
2727
const onSetRef = useSyncedRef(onSet);
2828
const onGetRef = useSyncedRef(onGet);
2929

src/useIntersectionObserver/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export type UseIntersectionObserverOptions = {
103103
* considered the viewport. Any part of the target not visible in the visible
104104
* area of the root is not considered visible.
105105
*/
106-
root?: RefObject<Element | Document> | Element | Document | null;
106+
root?: RefObject<Element | Document | null> | Element | Document | null;
107107
/**
108108
* A string which specifies a set of offsets to add to the root's bounding_box
109109
* when calculating intersections, effectively shrinking or growing the root
@@ -130,7 +130,7 @@ export type UseIntersectionObserverOptions = {
130130
* react reference
131131
*/
132132
export function useIntersectionObserver<T extends Element>(
133-
target: RefObject<T> | T | null,
133+
target: RefObject<T | null> | T | null,
134134
{
135135
threshold = DEFAULT_THRESHOLD,
136136
root: r,

src/useKeyboardEvent/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export type UseKeyboardEventOptions<T extends EventTarget> = {
1818
* Target element that emits `event`.
1919
* @default window
2020
*/
21-
target?: RefObject<T> | T | null;
21+
target?: RefObject<T | null> | T | null;
2222
/**
2323
* Options passed to the underlying `useEventListener` hook.
2424
*/

src/useLifecycleLogger/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,13 @@ export function useLifecycleLogger(componentName: string, deps?: DependencyList)
1111

1212
useEffect(() => {
1313
if (mountedRef.current) {
14-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1514
console.log(`${componentName} updated`, deps && [...deps]);
1615
}
1716
// eslint-disable-next-line react-hooks/exhaustive-deps
1817
}, deps);
1918

2019
useEffect(() => {
2120
mountedRef.current = true;
22-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
2321
console.log(`${componentName} mounted`, deps && [...deps]);
2422

2523
return () => {

src/useMap/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const proto = Map.prototype;
1212
export function useMap<K = any, V = any>(
1313
entries?: ReadonlyArray<readonly [K, V]> | null,
1414
): Map<K, V> {
15-
const mapRef = useRef<Map<K, V>>();
15+
const mapRef = useRef<Map<K, V>>(undefined);
1616
const rerender = useRerender();
1717

1818
if (!mapRef.current) {

src/useMediaQuery/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ const queryUnsubscribe = (query: string, setState: QueryStateSetter): void => {
5252
if (mql.removeEventListener) {
5353
mql.removeEventListener('change', listener);
5454
} else {
55-
// eslint-disable-next-line @typescript-eslint/no-deprecated
5655
mql.removeListener(listener);
5756
}
5857
}

src/usePrevious/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {useEffect, useRef} from 'react';
88
* @param value Value to yield on next render
99
*/
1010
export function usePrevious<T>(value?: T): T | undefined {
11-
const previous = useRef<T>();
11+
const previous = useRef<T>(undefined);
1212

1313
useEffect(() => {
1414
previous.current = value;

src/useResizeObserver/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ function getResizeObserver(): ResizeObserverSingleton | undefined {
8383
* @param enabled Whether resize observer is enabled or not.
8484
*/
8585
export function useResizeObserver<T extends Element>(
86-
target: RefObject<T> | T | null,
86+
target: RefObject<T | null> | T | null,
8787
callback: UseResizeObserverCallback,
8888
enabled = true,
8989
): void {

src/useSet/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const proto = Set.prototype;
1010
*/
1111

1212
export function useSet<T = any>(values?: readonly T[] | null): Set<T> {
13-
const setRef = useRef<Set<T>>();
13+
const setRef = useRef<Set<T>>(undefined);
1414
const rerender = useRerender();
1515

1616
if (!setRef.current) {

src/useThrottledCallback/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ export function useThrottledCallback<Fn extends (...args: any[]) => any>(
2222
delay: number,
2323
noTrailing = false,
2424
): ThrottledFunction<Fn> {
25-
const timeout = useRef<ReturnType<typeof setTimeout>>();
26-
const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>();
25+
const timeout = useRef<ReturnType<typeof setTimeout>>(undefined);
26+
const lastCall = useRef<{args: Parameters<Fn>; this: ThisParameterType<Fn>}>(undefined);
2727

2828
useUnmountEffect(() => {
2929
if (timeout.current) {
@@ -68,6 +68,6 @@ export function useThrottledCallback<Fn extends (...args: any[]) => any>(
6868
});
6969

7070
return wrapped;
71-
// eslint-disable-next-line react-hooks/exhaustive-deps,@typescript-eslint/no-unsafe-assignment
71+
// eslint-disable-next-line react-hooks/exhaustive-deps
7272
}, [delay, noTrailing, ...deps]);
7373
}

yarn.lock

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,8 +1283,8 @@ __metadata:
12831283
"@react-hookz/eslint-formatter-gha": "npm:^3.0.4"
12841284
"@testing-library/react-hooks": "npm:^8.0.1"
12851285
"@types/js-cookie": "npm:^3.0.6"
1286-
"@types/react": "npm:^17.0.83"
1287-
"@types/react-dom": "npm:^17.0.26"
1286+
"@types/react": "npm:^19.0.10"
1287+
"@types/react-dom": "npm:^19.0.4"
12881288
"@vitest/coverage-v8": "npm:^3.0.8"
12891289
commitlint: "npm:^19.7.1"
12901290
eslint: "npm:^9.21.0"
@@ -1301,8 +1301,8 @@ __metadata:
13011301
vitest: "npm:^3.0.8"
13021302
peerDependencies:
13031303
js-cookie: ^3.0.5
1304-
react: ^16.8 || ^17 || ^18
1305-
react-dom: ^16.8 || ^17 || ^18
1304+
react: ^16.8 || ^17 || ^18 || ^19
1305+
react-dom: ^16.8 || ^17 || ^18 || ^19
13061306
peerDependenciesMeta:
13071307
js-cookie:
13081308
optional: true
@@ -1813,37 +1813,21 @@ __metadata:
18131813
languageName: node
18141814
linkType: hard
18151815

1816-
"@types/prop-types@npm:*":
1817-
version: 15.7.14
1818-
resolution: "@types/prop-types@npm:15.7.14"
1819-
checksum: 10c0/1ec775160bfab90b67a782d735952158c7e702ca4502968aa82565bd8e452c2de8601c8dfe349733073c31179116cf7340710160d3836aa8a1ef76d1532893b1
1820-
languageName: node
1821-
linkType: hard
1822-
1823-
"@types/react-dom@npm:^17.0.26":
1824-
version: 17.0.26
1825-
resolution: "@types/react-dom@npm:17.0.26"
1816+
"@types/react-dom@npm:^19.0.4":
1817+
version: 19.0.4
1818+
resolution: "@types/react-dom@npm:19.0.4"
18261819
peerDependencies:
1827-
"@types/react": ^17.0.0
1828-
checksum: 10c0/8363921f08afe3f2ef82fe293301a0809ec646975fe9f5bfeb2e823f7237b97e47d27e1c6c2ffff27d15c12ab3cad1de6c77a737e37499fcc52793b0fd674f3f
1820+
"@types/react": ^19.0.0
1821+
checksum: 10c0/4e71853919b94df9e746a4bd73f8180e9ae13016333ce9c543dcba9f4f4c8fe6e28b038ca6ee61c24e291af8e03ca3bc5ded17c46dee938fcb32d71186fda7a3
18291822
languageName: node
18301823
linkType: hard
18311824

1832-
"@types/react@npm:^17.0.83":
1833-
version: 17.0.83
1834-
resolution: "@types/react@npm:17.0.83"
1825+
"@types/react@npm:^19.0.10":
1826+
version: 19.0.10
1827+
resolution: "@types/react@npm:19.0.10"
18351828
dependencies:
1836-
"@types/prop-types": "npm:*"
1837-
"@types/scheduler": "npm:^0.16"
18381829
csstype: "npm:^3.0.2"
1839-
checksum: 10c0/c8f76790190a9df42099f5f78d08dd4095f2da3bd97ff7cce0001d5a97ff3ffb31f703575acf2c457606e0d0b229ca8d1ba0ff459b77a4e44c5ea5154fe3fb4b
1840-
languageName: node
1841-
linkType: hard
1842-
1843-
"@types/scheduler@npm:^0.16":
1844-
version: 0.16.8
1845-
resolution: "@types/scheduler@npm:0.16.8"
1846-
checksum: 10c0/f86de504945b8fc41b1f391f847444d542e2e4067cf7e5d9bfeb5d2d2393d3203b1161bc0ef3b1e104d828dabfb60baf06e8d2c27e27ff7e8258e6e618d8c4ec
1830+
checksum: 10c0/41884cca21850c8b2d6578b172ca0ca4fff6021251a68532b19f2031ac23dc5a9222470208065f8d9985d367376047df2f49ece8d927f7d04cdc94922b1eb34b
18471831
languageName: node
18481832
linkType: hard
18491833

0 commit comments

Comments
 (0)