Skip to content

Commit f7a2354

Browse files
committed
main 🧊 rework use element size
1 parent fba1334 commit f7a2354

File tree

5 files changed

+55
-48
lines changed

5 files changed

+55
-48
lines changed

‎src/hooks/useElementSize/useElementSize.demo.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ const Demo = () => {
77
<div className='flex flex-col gap-4'>
88
<p>Resize the box to see changes</p>
99
<textarea
10-
ref={elementSize.ref}
1110
disabled
11+
ref={elementSize.ref}
1212
className='h-[200px] w-[200px]'
1313
style={{ resize: 'both' }}
14-
value={`width: ${elementSize.value.width}\nheight: ${elementSize.value.height}`}
14+
value={`width: ${Math.floor(elementSize.value.width)}\nheight: ${Math.floor(elementSize.value.height)}`}
1515
/>
1616
</div>
1717
);

‎src/hooks/useElementSize/useElementSize.test.ts‎

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type { UseElementSizeReturn } from './useElementSize';
99
import { useElementSize } from './useElementSize';
1010

1111
const trigger = createTrigger<ResizeObserverCallback, Element>();
12+
const mockGetBoundingClientRect = vi.spyOn(Element.prototype, 'getBoundingClientRect');
1213
const mockResizeObserverDisconnect = vi.fn();
1314
const mockResizeObserverObserve = vi.fn();
1415
const mockResizeObserver = class ResizeObserver {
@@ -32,7 +33,6 @@ const mockResizeObserver = class ResizeObserver {
3233

3334
beforeEach(() => void vi.stubGlobal('ResizeObserver', mockResizeObserver));
3435
afterEach(() => void vi.unstubAllGlobals());
35-
afterEach(() => void vi.restoreAllMocks());
3636

3737
const targets = [
3838
undefined,
@@ -49,6 +49,7 @@ targets.forEach((target) => {
4949

5050
describe(`${target}`, () => {
5151
it('Should use element size', () => {
52+
mockGetBoundingClientRect.mockImplementation(() => new DOMRect(0, 0, 0, 0));
5253
const { result } = renderHook(() => {
5354
if (target)
5455
return useElementSize(target) as {
@@ -61,19 +62,23 @@ targets.forEach((target) => {
6162
});
6263

6364
it('Should set initial value', () => {
65+
mockGetBoundingClientRect.mockImplementation(() => new DOMRect(0, 0, 200, 200));
6466
const { result } = renderHook(() => {
65-
if (target) return useElementSize(target, { width: 200, height: 200 });
66-
return useElementSize<HTMLDivElement>({ width: 200, height: 200 });
67+
if (target)
68+
return useElementSize(target) as {
69+
ref: StateRef<HTMLDivElement>;
70+
} & UseElementSizeReturn;
71+
return useElementSize<HTMLDivElement>();
6772
});
6873

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-
}
74+
if (!target)
75+
act(() => result.current.ref(document.getElementById('target')! as HTMLDivElement));
76+
77+
expect(result.current.value).toStrictEqual({ width: 200, height: 200 });
7478
});
7579

7680
it('Should change value after resize', () => {
81+
mockGetBoundingClientRect.mockImplementation(() => new DOMRect(0, 0, 0, 0));
7782
const { result } = renderHook(() => {
7883
if (target)
7984
return useElementSize(target) as {
@@ -91,20 +96,17 @@ targets.forEach((target) => {
9196
const element = (target ? getElement(target) : result.current.ref.current) as Element;
9297
if (!element) return;
9398

94-
vi.spyOn(Element.prototype, 'getBoundingClientRect').mockImplementation(
95-
() => new DOMRect(0, 0, 200, 200)
96-
);
99+
mockGetBoundingClientRect.mockImplementation(() => new DOMRect(0, 0, 200, 200));
97100

98-
trigger.callback(element, [
99-
{ contentRect: { width: 200, height: 200 } }
100-
] as unknown as ResizeObserverEntry[]);
101+
trigger.callback(element);
101102
});
102103

103104
expect(mockResizeObserverObserve).toHaveBeenCalledTimes(1);
104105
expect(result.current.value).toStrictEqual({ width: 200, height: 200 });
105106
});
106107

107108
it('Should disconnect on onmount', () => {
109+
mockGetBoundingClientRect.mockImplementation(() => new DOMRect(0, 0, 0, 0));
108110
const { result, unmount } = renderHook(() => {
109111
if (target)
110112
return useElementSize(target) as {

‎src/hooks/useElementSize/useElementSize.ts‎

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,9 @@ export interface UseElementSizeReturn {
2626
}
2727

2828
export interface UseElementSize {
29-
<Target extends UseElementSizeTarget>(
30-
target: Target,
31-
initialValue?: UseElementSizeValue
32-
): UseElementSizeReturn;
29+
<Target extends UseElementSizeTarget>(target: Target): UseElementSizeReturn;
3330

3431
<Target extends UseElementSizeTarget>(
35-
initialValue?: UseElementSizeValue,
3632
target?: never
3733
): { ref: StateRef<Target> } & UseElementSizeReturn;
3834
}
@@ -45,47 +41,39 @@ export interface UseElementSize {
4541
* @overload
4642
* @template Target The target element type
4743
* @param {UseElementSizeTarget} target The target element to observe
48-
* @param {UseElementSizeValue} [initialValue] The initial size of the element.
4944
* @returns {UseElementSizeReturn} An object containing the current width and height of the element
5045
*
5146
* @example
5247
* const { value } = useElementSize(ref);
5348
*
5449
* @overload
55-
* @param {UseElementSizeValue} [initialValue] The initial size of the element
5650
* @returns { { ref: StateRef<Target> } & UseElementSizeReturn } An object containing the current width and height of the element
5751
*
5852
* @example
5953
* const { ref, value } = useElementSize();
6054
*/
6155
export const useElementSize = ((...params: any[]) => {
6256
const target = (isTarget(params[0]) ? params[0] : undefined) as UseElementSizeTarget | undefined;
63-
const initialValue = (target ? params[1] : params[0]) as UseElementSizeValue | undefined;
64-
65-
const [size, setSize] = useState(initialValue ?? { width: 0, height: 0 });
57+
const [size, setSize] = useState({ width: 0, height: 0 });
6658
const internalRef = useRefState<Element>();
6759

6860
useIsomorphicLayoutEffect(() => {
6961
const element = (target ? getElement(target) : internalRef.current) as Element;
7062

7163
if (!element) return;
7264

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-
};
65+
const { width, height } = element.getBoundingClientRect();
66+
setSize({
67+
width,
68+
height
69+
});
70+
71+
const observer = new ResizeObserver(() => {
72+
const { width, height } = element.getBoundingClientRect();
73+
setSize({ width, height });
74+
});
8575

86-
const observer = new ResizeObserver(callback);
8776
observer.observe(element);
88-
callback();
8977

9078
return () => {
9179
observer.disconnect();

‎src/hooks/useRefState/useRefState.test.ts‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,22 @@ it('Should trigger rerender when the value is updated via current property', ()
5656

5757
expect(renderCount).toBe(2);
5858
});
59+
60+
const values = [1, {}];
61+
values.forEach((value) => {
62+
it(`Should trigger dont rerender when the value ${value} is equal to the current value`, () => {
63+
let renderCount = 0;
64+
const { result } = renderHook(() => {
65+
renderCount++;
66+
return useRefState(value);
67+
});
68+
69+
expect(renderCount).toBe(1);
70+
71+
act(() => {
72+
result.current.current = value;
73+
});
74+
75+
expect(renderCount).toBe(1);
76+
});
77+
});

‎src/hooks/useRefState/useRefState.ts‎

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

1918
Object.defineProperty(ref, 'current', {
2019
get() {
2120
return temp;
2221
},
2322
set(value: Value) {
24-
if (temp !== value) {
25-
temp = value;
26-
rerender();
27-
}
23+
if (temp === value) return;
24+
temp = value;
25+
rerender();
2826
},
2927
configurable: true,
3028
enumerable: true

0 commit comments

Comments
 (0)