강의를 듣다가 모달을 구현하는 두 가지 방식을 배웠다
import React, { useRef, useEffect } from 'react';
import { createPortal } from 'react-dom';
interface ModalProps {
open: boolean;
children: React.ReactNode;
onClose: () => void;
}
function Modal({ open, children, onClose }: ModalProps) {
const dialog = useRef<HTMLDialogElement | null>(null);
useEffect(() => {
if (open) {
dialog.current?.showModal();
} else {
dialog.current?.close();
}
}, [open]);
return createPortal(
<dialog className="modal" ref={dialog} onClose={onClose}>
{open ? children : null}
</dialog>,
document.getElementById('modal') as Element,
);
}
export default Modal;
State를 외부에 두고 직관적이고 간단하게 관리하는 방식
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
import { createPortal } from 'react-dom';
interface ModalProps {
children: React.ReactNode;
buttonCaption: string;
}
const Modal = forwardRef<{ open: () => void; close: () => void }, ModalProps>(
(
props: ModalProps,
ref: React.Ref<{ open: () => void; close: () => void }>
) => {
const dialog = useRef<HTMLDialogElement>(null);
const modalRoot = document.getElementById('modal-root');
const { children, buttonCaption } = props;
useImperativeHandle(ref, () => ({
open() {
dialog.current?.showModal();
},
close() {
dialog.current?.close();
},
}));
return createPortal(
<dialog ref={dialog}>
{children}
<form method="dialog">
<button>{buttonCaption}</button>
</form>
</dialog>,
modalRoot as Element
);
}
);
Modal.displayName = 'Modal';
export default Modal;
State를 내부에 두고 useImperativeHandle을 이용하여
상태 제어 함수를 외부에 노출을 해 제어하는 방식이다.
두 방법의 장단점은 다음과 같다
첫 번째 방법(외부에서 상태 관리)
장점
1. 단순하고 직관적인 상태 관리
단점
1. 컴포넌트 간 의존성 증가
- 부모 컴포넌트의 상태에 의존하게 된다, 이럴 경우 특정 부모 컴포넌트에 종속적이게 되어 재사용성이 낮아질 수 있다. 모달의 상태를 가지고 있는 컴포넌트가 한 화면에 한개만 렌더링 된다면 상관 없겠지만 2개 이상이 되는 순간부터 상태관리가 매우 어려워지게 된다.
2. 비즈니스 로직의 결합
3. 복잡한 로직 처리
두 번째 방법(내부에서 상태 관리, 외부에 메서드 제공)
장점
1. 모달 열기/닫기 메서드 제공
단점
1. 조금 더 복잡한 구현
2. 추가적인 코드 필요
- 모달을 사용하기 위해서는 부모 컴포넌트에서 ref를 설정해야 하고, 이를 통해 모달 제어해야 하므로 추가적인 코드 작성이 필요하다
내가 선택할 방법
두 번째 방법은 내가 모달창을 여러개 띄우는 웹은 만드는 경우가 아니라면 굳이 사용하지 않을 것 같고
첫 번째 방법을 Context API를 사용하여 전역에 모달의 상태를 만들어
1. 상태 충돌 가능성 감서
2. 상태 관리의 복잡성 감소
3. 재사용성 향상
4. 비즤스 로직의 결합 감소
등을 꾀한다
하지만 여전히
1. 상태 관리의 성능 문제
- Context API는 상태가 변경될 때 해당 Contex모두를 리렌더링 하기 때문에 성능에 영향을 끼칠 수 있다
2. 디버깅의 어려움
- 어느 컴포넌트에서 이벤트가 발생했는지 추적하기 어렵고, 여러 Provider를 사용할 경우 더 파악하기 힘들다
등의 문제가 생기는데
추후 Recoil을 도입하여
해당 문제들을 개선할 것이다