Skip to content

Commit

Permalink
✨ Feature(#46): 유틸 함수, 훅 로직 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
rhehfl committed Nov 4, 2024
1 parent 9ab243f commit cd77c94
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 16 deletions.
25 changes: 20 additions & 5 deletions src/hooks/useBeforeUnload.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
import { useEffect } from 'react';
import usePreservedCallback from './usePreservedCallback';

interface UseBeforeUnloadProps {
enabled?: boolean;
beforeUnloadAction?: (event: BeforeUnloadEvent) => void;
}

const useBeforeUnload = ({
enabled = true, // 기본적으로 이벤트 바인딩
beforeUnloadAction, // noop으로 기본 함수 설정도 가능
}: UseBeforeUnloadProps = {}): void => {
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
event.preventDefault();
return (event.returnValue = '');
};
// usePreservedCallback는 함수의 참조를 유지해주는 커스텀 훅입니다. @modern-kit 참고
// props로 전달하는 함수의 참조를 유지해야되는 이유는 함수도 결국 객체이기 때문에 props로 전달하는 함수는 리렌더링마다 재생성되기 때문에
// useCallback으로 관리하더라도 매번 재생성됩니다. 이런 문제를 해결하기 위해 usePreservedCallback와 같은 훅을 사용
//내부적으로 ref를 통해 최신 콜백함수 유지 useCallback을 통해 함수 참조값 메모이제이션
const handleBeforeUnload = usePreservedCallback(
(event: BeforeUnloadEvent) => {
event.preventDefault();

// beforeUnloadAction이 있을 경우 호출
if (beforeUnloadAction) {
beforeUnloadAction(event);
}
return (event.returnValue = '');
}
);

useEffect(() => {
if (!enabled) return; // enabled가 false이면 이벤트 바인딩하지 않음

Expand All @@ -19,6 +34,6 @@ const useBeforeUnload = ({
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, [enabled]);
}, [enabled, handleBeforeUnload]);
};
export default useBeforeUnload;
29 changes: 29 additions & 0 deletions src/hooks/usePreservedCallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useCallback, useRef } from 'react';
import noop from '../utils/noop';

/**
* @description 주어진 콜백 함수의 `참조를 유지`하고, 컴포넌트 렌더링 사이에 재사용할 수 있도록 도와주는 커스텀 훅입니다.
*
* 이 훅은 특히 콜백 함수가 렌더링 중에 변경될 때 유용합니다. 불필요한 함수 생성을 방지하고 최적화하며, 최신 버전의 콜백 함수를 사용 할 수 있습니다.
*
* @template T - 콜백 함수의 타입.
* @param {T | undefined} [callback=noop] - 유지하고자 하는 콜백 함수.
* @returns {T} 최신 콜백 함수 참조를 사용하는 메모이제이션된 콜백 함수.
*
* @example
* const preservedCallback = usePreservedCallback(callback);
*
* preservedCallback();
*/
const usePreservedCallback = <T extends (...args: any[]) => any>(
callback: T = noop as T
): T => {
const callbackRef = useRef<T>(callback);

callbackRef.current = callback;

return useCallback((...args: any[]) => {
return callbackRef.current(...args);
}, []) as T;
};
export default usePreservedCallback;
6 changes: 3 additions & 3 deletions src/pages/quiz/Quiz.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import ResultModal from '../../features/quiz/ui/ResultModal';
import getParams from '../../utils/getParams';
import TotalResults from '../../features/quiz/ui/TotalResults';
import { useState } from 'react';
import arraysEqual from '../../utils/arraysEqual';
import isArrayContentEqual from '../../utils/arraysEqual';
import { ResponseButton, SubmitSection } from '../../features/quiz/styles';
import QuizzesQuery from '../../queries/quizzesQuery';
import useMoadl from '../../hooks/useModal';
Expand All @@ -24,7 +24,7 @@ export default function Quiz() {
useClientQuizStore();
const [result, setResult] = useState<boolean>(false);
const { Modal, closeModal, openModal, isShow } = useMoadl();
//새로고침 시 WaringAlert가 뜨게해주는
//페이지 이탈시 경고창이 뜨는
useBeforeUnload();
//추후에 url에서 추출이 아닌 내부적으로 props로 전달하는 로직으로 변경 예정
const [partId] = getParams(['part-id']);
Expand Down Expand Up @@ -82,7 +82,7 @@ export default function Quiz() {
<ResponseButton
disabled={userResponseAnswer[0] === ''}
onClick={() => {
setResult(arraysEqual<string>(userResponseAnswer, answer));
setResult(isArrayContentEqual(userResponseAnswer, answer));
openModal();
}}
>
Expand Down
21 changes: 13 additions & 8 deletions src/utils/arraysEqual.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
/**
* @description 2개의 배열을 비교하는 함수입니다.
* @description 2개의 1차원 배열을 순서와 내부 값이 일치하는지 비교하는 함수입니다.
*
* @template T 배열 내부 요소의 타입
*
* @param T[] array 첫번째 array
* @param T[] array 두번째 array
* @param array1 첫번째 array
* @param array2 두번째 array
*
* @returns boolean값
*
* @example arraysEquial<number>([1,2,3,4,5],[1,2,3,4,1]);
* @example isArrayContentEqual([1,2,3,4,5],['a',2,3,1]);
* //false
*/
const arraysEqual = <T>(array1: T[], array2: T[]) => {
return JSON.stringify(array1) === JSON.stringify(array2);
const isArrayContentEqual = (array1: any[], array2: any[]): boolean => {
if (array1.length !== array2.length) return false;
for (let i = 0; i < array1.length; i++) {
if (array1[i] !== array2[i]) {
return false;
}
}
return true;
};
export default arraysEqual;
export default isArrayContentEqual;
10 changes: 10 additions & 0 deletions src/utils/noop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* @description 아무 작업도 수행하지 않는 함수입니다.
* 주로 콜백 함수나 기본 동작이 필요하지 않은 경우에 사용됩니다.
*
* @returns {void} - 아무런 값도 반환하지 않는 `Promise`를 반환합니다.
*
* @example
* noop(); // 이 함수는 아무런 작업도 하지 않고 종료됩니다.
*/
export default function noop(): void {}

0 comments on commit cd77c94

Please sign in to comment.