안녕하세요. 강의본 내용을 참고하여 별도의 라이브러리 없이 React-hooks로 Modal을 구현해보려고 합니다.
가장 먼저 react 앱을 생성해줍니다.
npx creaete-react-app modal-comp
이제 불필요한 부분을 전부 지우고 아래의 간단한 구조로 작업을 진행했습니다.
저는 클래스형 보다는 함수형이 작성, 보기가 편해서 함수형으로 작성해줬습니다.
모든 컴포넌트와 css 파일이 Index 한 곳으로 몰아줬습니다.
ReactDOM.render()
은 React 코드를 DOM(Document Object Model)에 적용해주는 놈입니다. 모바일(ios, aos)에는 ReactNative.render()
이 있습니다.
React.Strictmode
는 애플리케이션 내의 잠재적인 문제를 알아내기 위한 도구라는 공식문서에서 설명해줍니다. 굳이 사용하지 않을 이유가 없기에 지우지 않았습니다.
root
라는 엘리먼트 내에 렌더링됩니다.
const [showModal, setShowModal] = useState(false);
console.log(showModal); //false
5번 줄에 작성된 코드를 보시면 항상 상태의 초기값은 false입니다.
Modal창을 띄울때만 True로 바뀌게 됩니다.
const activeModal = () => {
setShowModal((open) => !open);
:
:
<button className="modal_btn" onClick={activeModal}>
Modal Open
</button>
:
:
};
7번 줄에서 상태를 바꿔줄 함수를 생성 해줬습니다.
12번 줄에 작성된 Button을 클릭할 경우 showModal의 상태가 true로 변경되면서 Modal 컴포넌트가 렌더링 됩니다.
<Modal showModal={showModal} setShowModal={setShowModal} />
Modal컴포넌트에서도 사용될 showModal
과 setShowModal
을 전달해줍니다.
import { useState, useRef, useCallback, useEffect } from "react";
1번 줄에서 필요한 부분을 react에서 가져와줬습니다.
전달받은 props를 파라미터로 중괄호, {}
안에 담아줬습니다.
const modalRef = useRef();
console.log(modalRef);
4번 줄 useRef()
함수를 modalRef 변수에 담아줬습니다. useRef()
는 current
라는 속성을 가진 객체를 반환해줍니다. 이 current 라는 속성을 변경해줘도 상태를 변경할 때 처럼 React 컴포넌트가 다시 랜더링 되지 않습니다. 현재는 아무것도 선택된게 없으므로 undefined
입니다.
const closeModal = (e) => {
if (modalRef.current === e.target) {
setShowModal(false);
}
};
7번 줄 함수는 modalRef.current
즉 useRef()
의 속성 current
의 값을 e.target
(현재 클릭한 부분)과 일치할 경우 상태를 False로 바꿔 모달을 닫는 기능을 가집니다.
const closeKey = useCallback(
(e) => {
if (e.key === "Escape" && showModal) {
setShowModal(false);
}
},
[setShowModal, showModal]
);
useEffect(() => {
document.addEventListener("keydown", closeKey);
return () => document.removeEventListener("keydown", closeKey);
}, [closeKey]);
13~25번 줄 코드는 ESC, Enter 키를 입력하게 되면 Modal 창이 닫히게 해주는 코드입니다. 여기서 useCallback() 함수를 사용해줬습니다. 보통 일방적으로 useCallback 없이도 구현이 가능하지만 굳이 사용한 이유는 어느 글을 통해 자세히 알게되었습니다. Modal이라는 컴포넌트가 다시 렌더링 될 경우 컴포넌트 안에서 선언된 함수들을 새로 생성하게 됩니다. 매번 렌더링 될 때마다 함수도 계속 생성되기 때문에 비효율적으로 동작하게됩니다.
const closeKey = useCallback(
(e) => {
if (e.key === "Escape" && showModal) {
setShowModal(false);
}
},
[setShowModal, showModal]
);
useCallback()
을 사용하게되면 현재의 상태(state) ShowModal
이 변경될 때만 함수를 재생성 해줍니다. 그러니까 coseKey
함수는 키 입력을 통해 모달을 닫아줘야하는 기능을 가졌기 때문에 결국 state에 의존하게됩니다. 그렇게 두번째 인자로 배열안에 담아주었고, useEffect부분을 보겠습니다.
useEffect(() => {
document.addEventListener("keydown", closeKey);
return () => document.removeEventListener("keydown", closeKey);
}, [closeKey]);
useEffect()
함수는 React 컴포넌트가 마운트
, 언마운트
, 업데이트
될 때 특정 작업을 실행합니다. useCallback()함수와 마찬가지로 두번째 의존 배열(CloseKey
)을 담아주어, 함수가 실행될 때마다 "Keydown" 이벤트를 생성해주고, Modal이 닫힘과 동시에 해당 이벤트를 다시 제거해 줍니다.
return (showModal ? ... : null)
대신
if (!showModal) return null;
return (
<div id="modal_box" ref={modalRef} onClick={closeModal}>
<div className="modal_contents">
<button
className="modal_close_btn"
onClick={() => setShowModal((modal) => !modal)}
>
✕
</button>
</div>
</div>
);
}
27~41번 줄에서 삼항연산자를 사용하여 네스팅을 하지 않고, 조건문을 사용하여 보기 편하게 작성했습니다.
감사합니다!