yarn create react-app react-modal
cd react-modal
yarn add styled-components prop-types react-icons react-daum-postcode
참고하여 모달 만들어 주세요
import React, { useCallback, useState } from "react";
import Modal from "../components/Modal";
import DaumPostcode from "react-daum-postcode";
function ModalPage() {
const [modalVisible, setModalVisible] = useState(false);
const [isOpenSecondPopup, setIsOpenSecondPopup] = useState(false);
const [address, setAddress] = useState(null);
const [postCodes, setPostCodes] = useState(null);
const [detailAddress, setDetailAddress] = useState("");
const openModal = useCallback(() => {
setModalVisible(true);
}, []);
const closeModal = useCallback(() => {
setModalVisible(false);
}, []);
const handleComplete = useCallback((data) => {
let fullAddress = data.address;
let extraAddress = "";
let zoneCodes = data.zonecode;
if (data.addressType === "R") {
if (data.bname !== "") {
extraAddress += data.bname;
}
if (data.buildingName !== "") {
extraAddress +=
extraAddress !== "" ? `, ${data.buildingName}` : data.buildingName;
}
fullAddress += extraAddress !== "" ? ` (${extraAddress})` : "";
}
//fullAddress -> 전체 주소반환
setAddress(fullAddress);
setPostCodes(zoneCodes);
setIsOpenSecondPopup(true);
}, []);
return (
<>
<h3 className="text">배송지</h3>
{address ? (
<div>
<div className="text">주소 : {address}</div>
<div className="text">우편번호 : {postCodes}</div>
</div>
) : (
<div className="text">
<span className="emph">배송지 입력을하고</span> <br />
배송유형을 확인해 보세요!
</div>
)}
<button onClick={openModal}>Open Modal</button>
{modalVisible && (
<Modal
visible={modalVisible}
closable={true}
maskClosable={true}
onClose={closeModal}
>
<DaumPostcode onComplete={handleComplete} className="post-code" />
</Modal>
)}
</>
);
}
export default ModalPage;
openModal 과 closeModal 함수를 통해서 버튼 클릭시 Modal을 열고 닫을수 있게 한다.
{address ? (
<div>
<div className="text">주소 : {address}</div>
<div className="text">우편번호 : {postCodes}</div>
</div>
) : (
<div className="text">
<span className="emph">배송지 입력을하고</span> <br />
배송유형을 확인해 보세요!
</div>
)}
그리고 address 주소가 존재할 때는 주소와 우편변호를 보여주고
null일경우 배송지를 입력하라는 글을 보여줌
버튼 클릭시, Modal 의 children 자리에 DaumPostcode component를 넣어
Modal 위에 띄우도록 만들었다.
import React, { useCallback, useState } from "react";
import Modal from "../components/Modal";
import DaumPostcode from "react-daum-postcode";
function ModalPage() {
(...)
const handleComplete = useCallback((data) => {
let fullAddress = data.address;
let extraAddress = "";
let zoneCodes = data.zonecode;
if (data.addressType === "R") {
if (data.bname !== "") {
extraAddress += data.bname;
}
if (data.buildingName !== "") {
extraAddress +=
extraAddress !== "" ? `, ${data.buildingName}` : data.buildingName;
}
fullAddress += extraAddress !== "" ? ` (${extraAddress})` : "";
}
//fullAddress -> 전체 주소반환
setAddress(fullAddress);
setPostCodes(zoneCodes);
setIsOpenSecondPopup(true);
}, []);
return (
(...)
<button onClick={openModal}>Open Modal</button>
{modalVisible && (
<Modal
visible={modalVisible}
closable={true}
maskClosable={true}
onClose={closeModal}
>
<DaumPostcode onComplete={handleComplete} className="post-code" />
</Modal>
)}
</>
);
}
export default ModalPage;
function ModalPage() {
const onChange = useCallback((e) => {
setDetailAddress(e.target.value);
}, []);
/*
여기 부분 추가
클릭시 Modal 창 닫기 && 상세페이지 닫기
*/
const onClick = useCallback(
(e) => {
e.preventDefault();
setAddress(address + detailAddress);
setIsOpenSecondPopup(false);
closeModal(false);
},
[closeModal, address, detailAddress, setAddress]
);
return (
<button onClick={openModal}>Open Modal</button>
{modalVisible && (
<Modal
visible={modalVisible}
closable={true}
maskClosable={true}
onClose={closeModal}
>
<DaumPostcode onComplete={handleComplete} className="post-code" />
/* 여기 부분 추가*/
{isOpenSecondPopup && (
<div>
<h3>상세 주소 입력</h3>
<input
placeholder="상세 주소를 입력해 주세요"
onChange={onChange}
value={detailAddress}
/>
<button onClick={onClick}>저장</button>
</div>
)}
</Modal>
)}
</>
);
}
export default ModalPage;
DaumPostcode component와 나란히 상세주소 페이지를 만들어 카카오 주소검색을 마친후
상세페이지가 볼수 있도록 했다.
import React, { useCallback, useState } from "react";
import Modal from "../components/Modal";
import DaumPostcode from "react-daum-postcode";
function ModalPage() {
const [modalVisible, setModalVisible] = useState(false);
const [isOpenSecondPopup, setIsOpenSecondPopup] = useState(false);
const [address, setAddress] = useState(null);
const [postCodes, setPostCodes] = useState(null);
const [detailAddress, setDetailAddress] = useState("");
const openModal = useCallback(() => {
setModalVisible(true);
}, []);
const closeModal = useCallback(() => {
setModalVisible(false);
}, []);
const onChange = useCallback((e) => {
setDetailAddress(e.target.value);
}, []);
const handleComplete = useCallback((data) => {
let fullAddress = data.address;
let extraAddress = "";
let zoneCodes = data.zonecode;
if (data.addressType === "R") {
if (data.bname !== "") {
extraAddress += data.bname;
}
if (data.buildingName !== "") {
extraAddress +=
extraAddress !== "" ? `, ${data.buildingName}` : data.buildingName;
}
fullAddress += extraAddress !== "" ? ` (${extraAddress})` : "";
}
//fullAddress -> 전체 주소반환
setAddress(fullAddress);
setPostCodes(zoneCodes);
setIsOpenSecondPopup(true);
}, []);
const onClick = useCallback(
(e) => {
e.preventDefault();
setAddress(address + detailAddress);
setIsOpenSecondPopup(false);
closeModal(false);
},
[closeModal, address, detailAddress, setAddress]
);
return (
<>
<h3 className="text">배송지</h3>
{address ? (
<div>
<div className="text">주소 : {address }</div>
<div className="text">우편번호 : {postCodes}</div>
</div>
) : (
<div className="text">
<span className="emph">배송지 입력을하고</span> <br />
배송유형을 확인해 보세요!
</div>
)}
<button onClick={openModal}>Open Modal</button>
{modalVisible && (
<Modal
visible={modalVisible}
closable={true}
maskClosable={true}
onClose={closeModal}
>
<DaumPostcode onComplete={handleComplete} className="post-code" />
{isOpenSecondPopup && (
<div>
<h3>상세 주소 입력</h3>
<input
placeholder="상세 주소를 입력해 주세요"
onChange={onChange}
value={detailAddress}
/>
<button onClick={onClick}>저장</button>
</div>
)}
</Modal>
)}
</>
);
}
export default ModalPage;
import React, { useEffect } from "react";
import PropTypes from "prop-types";
import styled from "styled-components";
import { GrClose } from "react-icons/gr";
import Portal from "./Portal";
function Modal({
className,
onClose,
maskClosable,
closable,
visible,
children,
}) {
const onMaskClick = (e) => {
if (e.target === e.currentTarget) {
onClose(e);
}
};
const close = (e) => {
if (onClose) {
onClose(e);
}
};
useEffect(() => {
document.body.style.cssText = `position: fixed; top: -${window.scrollY}px`;
return () => {
const scrollY = document.body.style.top;
document.body.style.cssText = `position: ""; top: "";`;
window.scrollTo(0, parseInt(scrollY || "0") * -1);
};
}, []);
return (
<Portal elementId="modal-root">
<ModalOverlay visible={visible} />
<ModalWrapper
className={className}
onClick={maskClosable ? onMaskClick : null}
tabIndex="-1"
visible={visible}
>
<ModalInner tabIndex="0" className="modal-inner">
<div className="close-btn">
{closable && <GrClose className="modal-close" onClick={close} />}
</div>
<br />
{children}
</ModalInner>
</ModalWrapper>
</Portal>
);
}
Modal.propTypes = {
visible: PropTypes.bool,
};
Modal.defaultProps = {
closable: true,
maskClosable: true,
visible: false,
};
const ModalWrapper = styled.div`
box-sizing: border-box;
display: ${(props) => (props.visible ? "block" : "none")};
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
overflow: auto;
outline: 0;
`;
const ModalOverlay = styled.div`
box-sizing: border-box;
display: ${(props) => (props.visible ? "block" : "none")};
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.6);
z-index: 999;
`;
const ModalInner = styled.div`
box-sizing: border-box;
position: relative;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5);
background-color: #fff;
border-radius: 10px;
width: 360px;
max-width: 480px;
top: 50%;
transform: translateY(-50%);
margin: 0 auto;
padding: 40px 20px;
.close-btn {
float: right;
}
`;
export default Modal;
import React, { useMemo } from "react";
import { createPortal } from "react-dom";
function Portal({ children, elementId }) {
const rootElement = useMemo(
() => document.getElementById(elementId),
[elementId]
);
return createPortal(children, rootElement);
}
export default Portal;
import React from "react";
import ModalPage from "./pages/ModalPage";
function App() {
return (
<div className="App">
<ModalPage />
</div>
);
}
export default App;
이 포스팅은 쿠팡 파트너스 활동의 일환으로 이에 따른 일정액의 수수료를 제공받을 수 있습니다.
출처: https://medium.com/@bestseob93/%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%AA%A8%EB%8B%AC-react-modal-%EB%A7%8C%EB%93%A4%EA%B8%B0-bd003458e9d
https://velog.io/@nangman/%EC%A3%BC%EC%86%8C-API%ED%99%9C%EC%9A%A9-KAKAO