Skip to content

Commit

Permalink
Merge pull request #8 from brgndyy/feat/#7
Browse files Browse the repository at this point in the history
usePreservedCallback 커스텀훅 작성 (issue #7)
  • Loading branch information
brgndyy authored Sep 23, 2024
2 parents fe36974 + 45dfd8e commit bc4cca4
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 2 deletions.
1 change: 0 additions & 1 deletion packages/react-metronome/.nvmrc

This file was deleted.

2 changes: 1 addition & 1 deletion packages/react/react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@brgndy/react",
"version": "1.0.1",
"version": "1.0.2",
"sideEffects": false,
"main": "dist/index.js",
"module": "dist/index.esm.js",
Expand Down
1 change: 1 addition & 0 deletions packages/react/react/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './useClickDetection';
export * from './usePreservedCallback';
25 changes: 25 additions & 0 deletions packages/react/react/src/hooks/usePreservedCallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# usePreservedCallback

참조하는 함수의 무결성을 유지하면서도 함수 내부에서 사용하는 상태나 값들이 변경될 때 최신 상태를 반영할 수 있도록 하는 훅입니다.

@params 무결성을 보장할 콜백 함수입니다.

## - Example

```tsx
export default function App() {
const [count, setCount] = useState(0);

const handleClick = usePreservedCallback(() => {
console.log(`현재 카운트는: ${count}`);
});

return (
<div>
<p>{count}</p>
<button onClick={handleClick}>클릭</button>
<button onClick={() => setCount(count + 1)}>증가 버튼</button>
</div>
);
}
```
93 changes: 93 additions & 0 deletions packages/react/react/src/hooks/usePreservedCallback.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { renderHook, act } from '@testing-library/react';
import usePreservedCallback from './usePreservedCallback';
import { useState } from 'react';
import { vi } from 'vitest';

describe('usePreservedCallback에 대한 테스트 코드 작성', () => {
it('만약 참조가 동일해도 가장 최근의 콜백 함수를 반환해야한다.', () => {
const { result, rerender } = renderHook(
({ callback }: { callback: (...args: any[]) => any }) => usePreservedCallback(callback),
{
initialProps: {
callback: () => 'initial',
},
},
);

expect(result.current()).toBe('initial');

rerender({ callback: () => 'updated' });

expect(result.current()).toBe('updated');
});

it('렌더링 후에도 같은 참조를 유지하고 있어야한다.', () => {
const { result, rerender } = renderHook(
({ callback }: { callback: (...args: any[]) => any }) => usePreservedCallback(callback),
{
initialProps: {
callback: vi.fn(),
},
},
);

const firstCallbackRef = result.current;

rerender({ callback: vi.fn() });

expect(result.current).toBe(firstCallbackRef);
});

it('컴포넌트에서 상태 값의 변화가 일어나면 그에 맞게 값이 올바르게 업데이트 되어야한다.', () => {
const { result } = renderHook(() => {
const [count, setCount] = useState(0);

const preservedCallback = usePreservedCallback(() => count);

return { preservedCallback, setCount };
});

expect(result.current.preservedCallback()).toBe(0);

act(() => {
result.current.setCount(10);
});

expect(result.current.preservedCallback()).toBe(10);
});

it('콜백이 인자를 올바르게 처리해야 한다', () => {
const { result, rerender } = renderHook(
({ callback }: { callback: (value: number) => string }) => usePreservedCallback(callback),
{
initialProps: {
callback: (value: number) => `initial ${value}`,
},
},
);

expect(result.current(1)).toBe('initial 1');

rerender({ callback: (value: number) => `updated ${value}` });

expect(result.current(2)).toBe('updated 2');
});

it('상태와 인자를 함께 처리해야 한다', () => {
const { result } = renderHook(() => {
const [prefix, setPrefix] = useState('initial');

const preservedCallback = usePreservedCallback((value: number) => `${prefix} ${value}`);

return { preservedCallback, setPrefix };
});

expect(result.current.preservedCallback(1)).toBe('initial 1');

act(() => {
result.current.setPrefix('updated');
});

expect(result.current.preservedCallback(2)).toBe('updated 2');
});
});
17 changes: 17 additions & 0 deletions packages/react/react/src/hooks/usePreservedCallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useCallback, useEffect, useRef } from 'react';

const usePreservedCallback = <Args extends unknown[], Return>(
callback: (...args: Args) => Return,
): ((...args: Args) => Return) => {
const callbackRef = useRef(callback);

useEffect(() => {
callbackRef.current = callback;
}, [callback]);

return useCallback((...args: Args): Return => {
return callbackRef.current(...args);
}, []);
};

export default usePreservedCallback;

0 comments on commit bc4cca4

Please sign in to comment.