Skip to content

Commit 742673d

Browse files
authored
Merge pull request #287 from alexxanderlee/fix/use-element-size-hook
Fix useElementSize hook
2 parents ad2f886 + 594f7b3 commit 742673d

File tree

3 files changed

+37
-14
lines changed

3 files changed

+37
-14
lines changed

src/hooks/useElementSize/useElementSize.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const mockResizeObserver = class ResizeObserver {
3232

3333
beforeEach(() => void vi.stubGlobal('ResizeObserver', mockResizeObserver));
3434
afterEach(() => void vi.unstubAllGlobals());
35+
afterEach(() => void vi.restoreAllMocks());
3536

3637
const targets = [
3738
undefined,
@@ -65,7 +66,11 @@ targets.forEach((target) => {
6566
return useElementSize<HTMLDivElement>({ width: 200, height: 200 });
6667
});
6768

68-
expect(result.current.value).toStrictEqual({ width: 200, height: 200 });
69+
if (!target) {
70+
expect(result.current.value).toStrictEqual({ width: 200, height: 200 });
71+
} else {
72+
expect(result.current.value).toStrictEqual({ width: 0, height: 0 });
73+
}
6974
});
7075

7176
it('Should change value after resize', () => {
@@ -86,6 +91,10 @@ targets.forEach((target) => {
8691
const element = (target ? getElement(target) : result.current.ref.current) as Element;
8792
if (!element) return;
8893

94+
vi.spyOn(Element.prototype, 'getBoundingClientRect').mockImplementation(
95+
() => new DOMRect(0, 0, 200, 200)
96+
);
97+
8998
trigger.callback(element, [
9099
{ contentRect: { width: 200, height: 200 } }
91100
] as unknown as ResizeObserverEntry[]);

src/hooks/useElementSize/useElementSize.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import type { RefObject } from 'react';
22

3-
import { useEffect, useState } from 'react';
3+
import { useState } from 'react';
44

55
import { getElement, isTarget } from '@/utils/helpers';
66

77
import type { StateRef } from '../useRefState/useRefState';
88

9+
import { useIsomorphicLayoutEffect } from '../useIsomorphicLayoutEffect/useIsomorphicLayoutEffect';
910
import { useRefState } from '../useRefState/useRefState';
1011

1112
/** The element size value type */
@@ -59,23 +60,32 @@ export interface UseElementSize {
5960
*/
6061
export const useElementSize = ((...params: any[]) => {
6162
const target = (isTarget(params[0]) ? params[0] : undefined) as UseElementSizeTarget | undefined;
62-
const initialValue = (target ? params[1] : params[0]) as UseElementSizeTarget | undefined;
63+
const initialValue = (target ? params[1] : params[0]) as UseElementSizeValue | undefined;
6364

6465
const [size, setSize] = useState(initialValue ?? { width: 0, height: 0 });
6566
const internalRef = useRefState<Element>();
6667

67-
useEffect(() => {
68-
if (!target && !internalRef.current) return;
68+
useIsomorphicLayoutEffect(() => {
6969
const element = (target ? getElement(target) : internalRef.current) as Element;
7070

7171
if (!element) return;
7272

73-
const observer = new ResizeObserver(([entry]) => {
74-
const { width, height } = entry.contentRect;
75-
setSize({ width, height });
76-
});
73+
const callback = () => {
74+
const rect = element.getBoundingClientRect();
75+
setSize((prev) => {
76+
if (prev.width !== rect.width || prev.height !== rect.height) {
77+
return {
78+
width: rect.width,
79+
height: rect.height
80+
};
81+
}
82+
return prev;
83+
});
84+
};
7785

78-
if (element) observer.observe(element);
86+
const observer = new ResizeObserver(callback);
87+
observer.observe(element);
88+
callback();
7989

8090
return () => {
8191
observer.disconnect();

src/hooks/useRefState/useRefState.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,21 @@ export interface StateRef<Value> {
1010
const createRefState = <Value>(initialValue: Value | undefined, rerender: () => void) => {
1111
let temp = initialValue;
1212
function ref(value: Value) {
13-
temp = value;
14-
rerender();
13+
if (temp !== value) {
14+
temp = value;
15+
rerender();
16+
}
1517
}
1618

1719
Object.defineProperty(ref, 'current', {
1820
get() {
1921
return temp;
2022
},
2123
set(value: Value) {
22-
rerender();
23-
temp = value;
24+
if (temp !== value) {
25+
temp = value;
26+
rerender();
27+
}
2428
},
2529
configurable: true,
2630
enumerable: true

0 commit comments

Comments
 (0)