Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions packages/core/src/utils/mergeRefs/ko/mergeRefs.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@
## 인터페이스

```ts
function mergeRefs<T>(
...refs: Array<RefObject<T> | RefCallback<T> | null | undefined>
): RefCallback<T>;
function mergeRefs<T>(...refs: Array<Ref<T> | undefined>): RefCallback<T>;
```

### 파라미터

<Interface
required
name="refs"
type="Array<RefObject<T> | RefCallback<T> | null | undefined>"
type="Array<Ref<T> | undefined>"
description="합쳐질 refs의 배열이에요. 각 ref는 RefObject 또는 RefCallback 중 하나일 수 있어요."
/>

Expand Down
6 changes: 2 additions & 4 deletions packages/core/src/utils/mergeRefs/mergeRefs.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ This function takes multiple refs (RefObject or RefCallback) and returns a singl
## Interface

```ts
function mergeRefs<T>(
...refs: Array<RefObject<T> | RefCallback<T> | null | undefined>
): RefCallback<T>;
function mergeRefs<T>(...refs: Array<Ref<T> | undefined>): RefCallback<T>;
```

### Parameters

<Interface
required
name="refs"
type="Array<RefObject<T> | RefCallback<T> | null | undefined>"
type="Array<Ref<T> | undefined>"
description="An array of refs to be merged. Each ref can be either a RefObject or RefCallback."
/>

Expand Down
52 changes: 51 additions & 1 deletion packages/core/src/utils/mergeRefs/mergeRefs.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useRef } from 'react';
import { act } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { describe, expect, it, vi } from 'vitest';

import { renderHookSSR } from '../../_internal/test-utils/renderHookSSR.tsx';

Expand Down Expand Up @@ -75,4 +75,54 @@ describe('mergeRefs', () => {
expect(result.current.ref1.current).toBe(value);
expect(ref3Value).toBe(value);
});

it('should call cleanup functions returned by callback refs', () => {
const cleanupCalls: string[] = [];

const callbackRef1 = vi.fn(() => {
return () => {
cleanupCalls.push('cleanup1');
};
});

const callbackRef2 = vi.fn(() => {
return () => {
cleanupCalls.push('cleanup2');
};
});

const mergedRef = mergeRefs<string | null>(callbackRef1, callbackRef2);
const value = 'test-value';

act(() => {
mergedRef(value);
});

const cleanupFn = mergedRef(null);
if (cleanupFn) {
cleanupFn();
}

expect(cleanupCalls).toEqual(['cleanup1', 'cleanup2']);
expect(callbackRef1).toHaveBeenCalledWith(value);
expect(callbackRef2).toHaveBeenCalledWith(value);
});

it('verifies that object refs initialize correctly without cleanup functions', () => {
const refObj = { current: 'initial' };
const mergedRef = mergeRefs(refObj);

act(() => {
mergedRef('new-value');
});
expect(refObj.current).toBe('new-value');

const cleanupFn = mergedRef(null);
expect(cleanupFn).toBeInstanceOf(Function);

if (cleanupFn) {
cleanupFn();
}
expect(refObj.current).toBeNull();
});
});
45 changes: 34 additions & 11 deletions packages/core/src/utils/mergeRefs/mergeRefs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { RefCallback, RefObject } from 'react';
import { Ref, RefCallback } from 'react';

type StrictRef<T> = NonNullable<Ref<T>>;
type RefCleanup<T> = ReturnType<RefCallback<T>>;

/**
* @description
Expand All @@ -7,7 +10,7 @@ import { RefCallback, RefObject } from 'react';
*
* @template T - The type of target to be referenced.
*
* @param {Array<RefObject<T> | RefCallback<T> | null | undefined>} refs - An array of refs to be merged. Each ref can be either a RefObject or RefCallback.
* @param {Array<Ref<T> | undefined>} refs - An array of refs to be merged. Each ref can be either a RefObject or RefCallback.
*
* @returns {RefCallback<T>} A single ref callback that updates all provided refs.
*
Expand All @@ -34,19 +37,39 @@ import { RefCallback, RefObject } from 'react';
* return <div ref={mergeRefs(measuredRef, ref)} />;
* }
*/
export function mergeRefs<T>(...refs: Array<RefObject<T> | RefCallback<T> | null | undefined>): RefCallback<T> {

function assignRef<T>(ref: StrictRef<T>, value: T | null): RefCleanup<T> {
if (typeof ref === 'function') {
return ref(value);
}

ref.current = value;
}

export function mergeRefs<T>(...refs: Array<Ref<T> | undefined>): RefCallback<T> {
const availableRefs = refs.filter(ref => ref != null);
const cleanupMap = new Map<StrictRef<T>, Exclude<RefCleanup<T>, void>>();

return value => {
for (const ref of refs) {
if (ref == null) {
continue;
for (const ref of availableRefs) {
const cleanup = assignRef(ref, value);
if (cleanup) {
cleanupMap.set(ref, cleanup);
}
}

if (typeof ref === 'function') {
ref(value);
continue;
return () => {
for (const ref of availableRefs) {
const cleanup = cleanupMap.get(ref);
if (cleanup && typeof cleanup === 'function') {
cleanup();
continue;
}

assignRef(ref, null);
}

(ref as RefObject<T | null>).current = value;
}
cleanupMap.clear();
};
};
}
Loading