Skip to content

Commit de2724a

Browse files
authored
fix: bad memoisation in window, screen dimension listener hooks (#2664)
* fix: bad memoisation in window, screen dimension listener hooks * remove unused variable
1 parent 9f873ca commit de2724a

File tree

2 files changed

+48
-49
lines changed

2 files changed

+48
-49
lines changed
Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { useEffect, useMemo, useState } from 'react';
2-
import { Dimensions } from 'react-native';
1+
import { useCallback, useEffect, useState } from 'react';
2+
import { Dimensions, ScaledSize } from 'react-native';
33

44
/**
55
* A custom hook that provides functions to calculate dimensions based on
@@ -9,35 +9,46 @@ import { Dimensions } from 'react-native';
99
* @returns {Object} An object containing functions vh and vw.
1010
*/
1111
export const useScreenDimensions = (rounded?: boolean) => {
12-
const [screenDimensions, setScreenDimensions] = useState(Dimensions.get('screen'));
12+
const [screenDimensions, setScreenDimensions] = useState(() => Dimensions.get('screen'));
1313

1414
useEffect(() => {
15-
const subscriptions = Dimensions.addEventListener('change', ({ screen }) => {
15+
const handleChange = ({ screen }: { screen: ScaledSize }) => {
1616
setScreenDimensions((prev) => {
1717
const { height, width } = screen;
1818
if (prev.height !== height || prev.width !== width) {
1919
return screen;
2020
}
2121
return prev;
2222
});
23-
});
23+
};
24+
const subscription = Dimensions.addEventListener('change', handleChange);
2425

25-
return () => subscriptions?.remove();
26-
}, []);
26+
// We might have missed an update between calling `get` in render and
27+
// `addEventListener` in this handler, so we set it here. If there was
28+
// no change, React will filter out this update as a no-op.
29+
// pattern ref: react-native-repo/packages/react-native/Libraries/Utilities/useWindowDimensions.js
30+
handleChange({ screen: Dimensions.get('screen') });
2731

28-
// eslint-disable-next-line react-hooks/exhaustive-deps
29-
const vw = (percentageWidth: number) => {
30-
const value = screenDimensions.width * (percentageWidth / 100);
31-
return rounded ? Math.round(value) : value;
32-
};
32+
return () => {
33+
subscription.remove();
34+
};
35+
}, []);
3336

34-
// eslint-disable-next-line react-hooks/exhaustive-deps
35-
const vh = (percentageHeight: number) => {
36-
const value = screenDimensions.height * (percentageHeight / 100);
37-
return rounded ? Math.round(value) : value;
38-
};
37+
const vw = useCallback(
38+
(percentageWidth: number) => {
39+
const value = screenDimensions.width * (percentageWidth / 100);
40+
return rounded ? Math.round(value) : value;
41+
},
42+
[rounded, screenDimensions.width],
43+
);
3944

40-
const screenDimensionFunctions = useMemo(() => ({ vh, vw }), [vh, vw]);
45+
const vh = useCallback(
46+
(percentageHeight: number) => {
47+
const value = screenDimensions.height * (percentageHeight / 100);
48+
return rounded ? Math.round(value) : value;
49+
},
50+
[rounded, screenDimensions.height],
51+
);
4152

42-
return screenDimensionFunctions;
53+
return { vh, vw };
4354
};

package/src/hooks/useViewport.ts

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { useEffect, useMemo, useState } from 'react';
2-
import { Dimensions } from 'react-native';
1+
import { useCallback } from 'react';
2+
import { useWindowDimensions } from 'react-native';
33

44
/**
55
* A custom hook that provides functions to calculate dimensions based on
@@ -9,35 +9,23 @@ import { Dimensions } from 'react-native';
99
* @returns {Object} An object containing functions vh and vw.
1010
*/
1111
export const useViewport = (rounded?: boolean) => {
12-
const [viewportDimensions, setViewportDimensions] = useState(Dimensions.get('window'));
12+
const viewportDimensions = useWindowDimensions();
1313

14-
useEffect(() => {
15-
const subscriptions = Dimensions.addEventListener('change', ({ window }) => {
16-
setViewportDimensions((prev) => {
17-
const { height, width } = window;
18-
if (prev.height !== height || prev.width !== width) {
19-
return window;
20-
}
21-
return prev;
22-
});
23-
});
14+
const vw = useCallback(
15+
(percentageWidth: number) => {
16+
const value = viewportDimensions.width * (percentageWidth / 100);
17+
return rounded ? Math.round(value) : value;
18+
},
19+
[rounded, viewportDimensions.width],
20+
);
2421

25-
return () => subscriptions?.remove();
26-
}, []);
22+
const vh = useCallback(
23+
(percentageHeight: number) => {
24+
const value = viewportDimensions.height * (percentageHeight / 100);
25+
return rounded ? Math.round(value) : value;
26+
},
27+
[rounded, viewportDimensions.height],
28+
);
2729

28-
// eslint-disable-next-line react-hooks/exhaustive-deps
29-
const vw = (percentageWidth: number) => {
30-
const value = viewportDimensions.width * (percentageWidth / 100);
31-
return rounded ? Math.round(value) : value;
32-
};
33-
34-
// eslint-disable-next-line react-hooks/exhaustive-deps
35-
const vh = (percentageHeight: number) => {
36-
const value = viewportDimensions.height * (percentageHeight / 100);
37-
return rounded ? Math.round(value) : value;
38-
};
39-
40-
const viewportFunctions = useMemo(() => ({ vh, vw }), [vh, vw]);
41-
42-
return viewportFunctions;
30+
return { vh, vw };
4331
};

0 commit comments

Comments
 (0)