-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
usePreservedCallback 커스텀훅 작성 (issue #7)
- Loading branch information
Showing
6 changed files
with
137 additions
and
2 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './useClickDetection'; | ||
export * from './usePreservedCallback'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
93
packages/react/react/src/hooks/usePreservedCallback.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |