React에서 modal창 만들기 (ReactPortal)
React modal 창 만들기
React에서 modal 창을 만드려고 한다.
현재까지 방법은 다음과 같다
- react portal을 이용
- react-modal 라이브러리 이용
- 순수하게 modal 창 만들기
이중에서 react-portal 이란 기능을 사용하기로 하였다.
React Portal이란?
Portal 는 리액트 프로젝트에서 컴포넌트를 렌더링할때, 렌더링 위치를 사전에 선택하여 부모 컴포넌트의 바깥에 렌더링 할 수 있게 해주는 기능이다.
1
2
3
4
5
6
7
...
<body>
<div id="root"></div>
<div id="modal"></div>
</body>
...
일반적인 React는 root div안에서 모든 컴포넌트를 생성, 활용, 재활용들을 한다.
그럼 ReactPortal은 무엇이냐? root div외부에 또 다른 div태그를 생성하여, 다른 div를 다루는 기능이다.
프로젝트로 들어가보자
components 폴더안에 - modal 창 폴더를 만든다.
code로 보기
ModalPortal.tsx
1
2
3
4
5
6
7
8
9
10
11
12
import ReactDOM from "react-dom";
interface ModalPortalProps {
children: React.ReactNode;
}
const ModalPortal = ({ children }: ModalPortalProps) => {
const modalRoot = document.getElementById("modal") as HTMLElement;
return ReactDOM.createPortal(children, modalRoot);
};
export default ModalPortal;
Modal 창을 외부돔에 설정하는 제일 기본적인 코드이다.
Modal.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import ModalPortal from "./ModalPortal";
import styled from "styled-components";
import CharacterAddModal from "./CharacterAddModal";
// 인자에서 modal 창을 닫을 수 있는 props를 가지고 온다.
type ModalProps = {
onClose: () => void;
};
const Background = styled.div`
z-index: 999;
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
position: fixed;
left: 0;
top: 0;
text-align: center;
background-color: #00000033;
`;
const ModalBox = styled.div`
width: 25rem;
background-color: white;
`;
const Modal = ({ onClose }: ModalProps) => {
return (
<>
<ModalPortal>
<Background>
<ModalBox>
// 안에서 구현한 components 를 입력해준다.
<CharacterAddModal onClose={onClose} />
</ModalBox>
</Background>
</ModalPortal>
</>
);
};
export default Modal;
실제로 구현될 모달 창의 형태이다. return 의 요소로 ModalPortal.tsx가 하위 요소를 감싸주는 역할이다.
CharacterAddModal.tsx은 하위요소에서 구현될 컴포넌트이다.
onClose는 모달이 닫히는 함수이다.
CharacterAddModal.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { AddsCharacter } from "modules/CharacterSchedule";
import { ChangeEvent, FormEvent, useState } from "react";
import { useDispatch } from "react-redux";
type CharacterData = {
characterName: string;
characterJob: string;
characterLevel: number;
guardianRestGage: number;
choseDungeonRestGage: number;
};
type CharacterAddModalProps = {
onClose: () => void;
};
const CharacterAddModal = ({ onClose }: CharacterAddModalProps) => {
const dispatch = useDispatch();
const onSubmit = (e: FormEvent) => {
dispatch(
AddsCharacter({
CharacterName: characterData.characterName,
Level: characterData.characterLevel,
Job: characterData.characterJob,
GaurdianRestGage: characterData.guardianRestGage,
ChaosDungeonRestGage: characterData.choseDungeonRestGage
})
);
e.preventDefault();
onClose();
};
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
const { id, value } = e.target;
setCharacterData({ ...characterData, [id]: value });
};
const [characterData, setCharacterData] = useState<CharacterData>({
characterName: "",
characterJob: "",
characterLevel: 0,
guardianRestGage: 0,
choseDungeonRestGage: 0
});
const {
characterName,
characterJob,
characterLevel,
guardianRestGage,
choseDungeonRestGage
} = characterData;
return (
<>
<form onSubmit={(e) => onSubmit(e)}>
<h3>이름</h3>
<input
id="characterName"
placeholder="케릭터 이름을 입력해주세요."
value={characterName}
onChange={onChange}
/>
<h3>직업</h3>
<input
id="characterJob"
placeholder="케릭터 직업을 입력해주세요."
value={characterJob}
onChange={onChange}
/>
<h3>레벨</h3>
<input
id="characterLevel"
placeholder="케릭터 레벨을 입력해주세요."
value={characterLevel}
onChange={onChange}
/>
<h3>카던 휴게</h3>
<input
id="guardianRestGage"
placeholder="가디언 휴식게이지를 입력해주세요."
value={guardianRestGage}
onChange={onChange}
/>
<h3>가디언토벌 휴게</h3>
<input
id="choseDungeonRestGage"
placeholder="카오스던전 휴식게이지를 입력해주세요."
value={choseDungeonRestGage}
onChange={onChange}
/>
<button type="submit">등록</button>
<button onClick={onClose}>나가기</button>
</form>
</>
);
};
export default CharacterAddModal;
하위요소 구성요소이다.
모달 창을 open 하는 버튼 code
AddCharacterButton.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import Modal from "components/modal/Modal";
import { useState } from "react";
import styled from "styled-components";
const AddCharacterButtonDiv = styled.div`
display: flex;
justify-content: center;
align-items: center;
`;
const AddButton = styled.button`
width: 100px;
height: 100px;
border-radius: 10px;
`;
const AddCharacterButton = () => {
// 1.-------------------------
const [ModalOpen, setModalOpen] = useState<boolean>(false);
const modalOn = () => {
setModalOpen(true);
};
const modalClose = () => {
setModalOpen(false);
};
return (
// 2.-------------------------
<AddCharacterButtonDiv>
<AddButton onClick={modalOn}>추가하기</AddButton>
{ModalOpen && <Modal onClose={modalClose} />}
</AddCharacterButtonDiv>
);
};
- useState 변수로 모달창 관리
1
2
3
4
5
6
7
8
const [ModalOpen, setModalOpen] = useState<boolean>(false);
const modalOn = () => {
setModalOpen(true);
};
const modalClose = () => {
setModalOpen(false);
};
- useState 변수 클릭시 modal 창 화면에 구현
1
2
3
4
<AddButton onClick={modalOn}>추가하기</AddButton>;
{
ModalOpen && <Modal onClose={modalClose} />;
}
참조링크
- react Portal
https://velog.io/@song961003/React-모달-여러개-띄우기
- 리액트 포탈 모달 스크롤 막기, createPortal
https://velog.io/@do_dadu/React에서-Modal-구현하기feat.-createPortal-스크롤-막기
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.