모달 컴포넌트를 만들어서 사용하는데, 모달의 렌더링 여부가 결정되는 state
를 모달 컴포넌트에게 위임할 수 없을까하는 생각이 들었다.
기존 코드 예시
import { useState } from "react";
import Modal from "../components/modal/modal";
export default function App() {
const [isOpen, setIsOpen] = useState(false);
const open = () => {
setIsOpen(() => true);
};
const close = () => {
setIsOpen(() => false);
};
return (
<div>
<button onClick={open}>open modal</button>
<Modal open={isOpen}>
<p>삭제하시겠습니까?</p>
<div>
<button onClick={close}>OK</button>
<button onClick={close}>Cancle</button>
</div>
</Modal>
</div>
)
}
Modal
컴포넌트는 props로 받은 isOpen
이 true일 경우에만 모달을 보여주고, false인 경우 null을 반환해서 아무것도 렌더링 되지 않도록 했다.
이렇게하면 모달을 사용하는 모든 화면 혹은 컴포넌트에서 모달의 상태 값을 가지고 있어야 하는데, 이 부분이 너무 효율적이지 않았다.
이럴 때는
hook
을 만들어서 사용하자!
먼저 모달 컴포넌트를 수정했다.
기존에는 props로 isOpen
을 받아서 렌더링 여부를 모달 컴포넌트 내부에서 결정했지만, 이 일을 hook
에게 위임할 예정이다.
// css
const modalWrapStyle = {
width: '100%',
height: '100vh',
overflow: 'hidden',
position: 'fixed',
top: 0, left: 0,
backgroundColor: 'rgba(0,0,0,0.3)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
textAlign: 'center'
}
const modalStyle = {
width: 380,
padding: 20,
backgroundColor: '#fff'
}
// 어떠한 작업도 하지 않고 모달을 보여주는 역할만 함
export default function Modal({ onClose, children }) {
return (
<div
className="modal"
style={{...modalWrapStyle}}
onClick={onClose}
>
<div
className="modal_container"
style={{...modalStyle}}
onClick={(e) => e.stopPropagation()}
>
{children}
</div>
</div>
)
}
class명이 modal
인 div는 모달의 외부 배경이다.
배경을 클릭하면 모달이 닫히도록 할 것이기 때문에 onClick
이벤트 발생시 hook에서 받아온 onClose
함수를 호출해서 모달을 닫는다.
class명이 modal_container
인 div가 모달이다.
modal_container
를 클릭하면 부모에게 이벤트가 전달되기 때문에 onClick={(e) => e.stopPropagation()}
로 이벤트가 전달되는 것을 막았다.
모달의 상태 값을 가지며 제어할 수 있는 useModal
hook이다.
import React, { useCallback, useState } from 'react';
import Modal from '../components/modal/modal';
// `useBlur` props로 모달 외부를 클릭하면 모달을 닫을지 선택하도록 했다.
const useModal = ({ useBlur = true } = {}) => {
// 모달의 렌더링 여부를 설정할 상태 값
const [isOpen, setIsOpen] = useState(false);
// 모달 열기
const open = useCallback(() => {
setIsOpen(() => true);
}, []);
// 모달 닫기
const close = useCallback(() => {
setIsOpen(() => false);
}, []);
// isOpen이 true라면 Modal 컴포넌트를 반환, false라면 null을 반환
return {
Modal: isOpen
? ({ children }) => (
<Modal onClose={useBlur ? close : null}>{children}</Modal>
)
: () => null,
open,
close,
isOpen,
};
};
export default useModal;
return 할 때 자체적으로 isOpen
을 체크해서 true
일 경우에만 Modal 컴포넌트를 반환해주고, false
인 경우에는 null을 반환하도록 한다.
useModal hook을 사용하면 이렇게 간단하게 모달 컴포넌트를 사용할 수 있게 된다.
import useModal from '../hooks/useModal';
export default function App() {
const { Modal, open, close } = useModal();
return (
<div>
<button onClick={open}>open modal</button>
<Modal>
<p>삭제하시겠습니까?</p>
<div>
<button onClick={close}>OK</button>
<button onClick={close}>Cancle</button>
</div>
</Modal>
</div>
)
}
isOpen
으로 모달이 보여지는지 체크할 필요가 없어졌으며, Modal
컴포넌트는 isOpen
이 true일 때만 보여지는 것을 보장받는다.
안녕하세요 좋은 글 잘 봤습니다.
글 내용 중 궁금한 점이 있어 질문 드립니다!
css를 style prop으로 전달하실 때 spread 연산자를 사용하지 않고
style={modalStyle}
이런 방식으로 하는 것이 더 간결하다고 생각하는데 spread 연산자를 사용하신 이유가 있으신가요?