티스토리 뷰
인프런 React 강의 듣고 사이트 만들기 _ Front 작업 06. Modal 팝업, 외부(document) 클릭 시 닫히게 만들기
주섬이 2021. 1. 25. 21:12
Modal 팝업 작업을 진행했습니다.
일단 공식문서에서 useRef, useImperativeHandle, forwardRef를 확인해보세요!
(저는 useRef로만 작업 했습니다.)
ko.reactjs.org/docs/hooks-reference.html#useref
ko.reactjs.org/docs/hooks-reference.html#useimperativehandle
ko.reactjs.org/docs/react-api.html#reactforwardref
useImperativeHandle과 forwardRef... 사용해서 해보려고도 하다가..
이것저것 하느라 시간이 걸렸습니다.
컴포넌트 형식 작업도 약간 헷갈리기도 하고^^;
그러다가 블로거님의 코드를 보고 짜잔 해결했습니다.
아래는 참고한 원본 코드의 주소입니다... 감사합니당(--)(__)(--)!
https://4log.hyeon.pro/post/click-event-outside-the-component
해당컴포넌트 outside 영역 클릭 이벤트
구현하고싶은 것은 Modal 을 구현하고싶다. 모달팝업이 띄워졌을때 바깥 영역을 클릭 하였을 때 이벤트를 발생시켜서 모달팝업의 상태를 close 로 변경하고싶다. 바깥 영역을 클릭 이벤트를 구현
4log.hyeon.pro
참고하여 코드 수정하여 적용한 모습입니다.
부모 컴포넌트입니다.
./Menu
import React, { useRef, useEffect, useCallback, useState } from "react";
import styled from 'styled-components';
import MenuPopup from './MenuPopup';
import { MenuOutlined } from '@ant-design/icons';
const MenuWrap = styled.div`
position: relative;
`;
const MenuButton = styled.button`
padding: 0;
background: none;
border: none;
cursor: pointer;
outline: none;
&:hover,
&:focus {
background: none;
}
&:hover,
&:focus,
&.active{
opacity: 0.5;
}
`;
const MenuIcon = styled(MenuOutlined)`
font-size: 17px;
color: ${props => props.themecolor};
`;
const Menu = ({ themecolor }) => {
const popRef = useRef(null);
const [isOpen, setIsOpen] = useState(false);
const onClickOutside = useCallback(({ target }) => {
if (popRef.current && !popRef.current.contains(target)) {
setIsOpen(false);
}
}, []);
const onClickMenu = useCallback(() => {
setIsOpen(!isOpen);
}, [isOpen]);
useEffect(() => {
document.addEventListener("click", onClickOutside);
return () => {
document.removeEventListener("click", onClickOutside);
};
}, []);
return(
<MenuWrap ref={popRef}>
<MenuButton onClick={onClickMenu}>
<MenuIcon themecolor={themecolor} />
</MenuButton>
<MenuPopup isOpen={isOpen} />
</MenuWrap>
);
};
export default Menu;
자식 컴포넌트입니다.
./MenuPopupWrap
import React from 'react';
import styled from 'styled-components';
const MenuPopupWrap = styled.div`
position: absolute;
top: 30px;
right: 0;
padding-top: 15px;
display: none;
width: 80px;
height: 125px;
background: rgba(0, 0, 0, 0.4);
clip-path: polygon(90% 10%,100% 10%,100% 100%,0 100%,0 10%,74% 10%,90% 0);
&.active {
display: block;
}
`;
const MenuPopup = ({ isOpen }) => {
return (
<MenuPopupWrap className={isOpen ? 'active' : ''}>
<ul>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
</ul>
</MenuPopupWrap>
);
};
export default MenuPopup;
(완성된 코드들은 아닙니다)
여기서 참고해야할 것을 간추리자면
부모컴포넌트에서..
const Menu = ({ themecolor }) => {
// 1. ref로 클릭한 타겟이 팝업인지 아닌지 체크할 것입니다.
const popRef = useRef(null);
const [isOpen, setIsOpen] = useState(false);
// 2. document에 바인딩할 클릭 이벤트입니다. (4번)
const onClickOutside = useCallback(({ target }) => {
if (popRef.current && !popRef.current.contains(target)) {
setIsOpen(false);
}
}, []);
// 3. 팝업 여닫는 이벤트입니다.
const onClickMenu = useCallback(() => {
setIsOpen(!isOpen);
}, [isOpen]); // 4. state를 넣어줘야 업데이트 됩니다.
// 4. 컴포넌트 마운팅될때 document 이벤트를(2번) 바인딩해줍니다.
useEffect(() => {
document.addEventListener("click", onClickOutside);
// 5. return을 통해 cleanup해줘야합니다.
// 안해주면 언마운팅이나 업데이트 시 문제가 생깁니다.
return () => {
document.removeEventListener("click", onClickOutside);
};
}, []);
return(
// 6. 오픈한 버튼이나 팝업 영역 모두 클릭 시 닫히면 안되기때문에...
// 전체 영역에 ref를 추가합니다.
<MenuWrap ref={popRef}>
// 7. 팝업 여닫는버튼입니다. (3번)
<MenuButton onClick={onClickMenu}>
<MenuIcon themecolor={themecolor} />
</MenuButton>
// 8. 팝업컨텐츠가 포함된 컴포넌트입니다.
<MenuPopup isOpen={isOpen} />
</MenuWrap>
);
};
export default Menu;
자식컴포넌트에서..
import React from 'react';
import styled from 'styled-components';
const MenuPopupWrap = styled.div`
display: none;
// 2. active일 경우 block처리하는 형식으로 했습니다.
// 이 부분은 부모 컴포넌트에서 처리해도됩니다.
&.active {
display: block;
}
`;
// 1. props를 받아와서 className을 추가합니다.
// 이 부분은 props를 통해 처리하지 않고 부모 컴포넌트에서 처리해도됩니다.
const MenuPopup = ({ isOpen }) => {
return (
<MenuPopupWrap
className={isOpen ? 'active' : ''}
>
<ul>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
</ul>
</MenuPopupWrap>
);
};
export default MenuPopup;
return 을 통해 뒷정리하는 부분 참고글!
velog.io/@velopert/react-hooks#23-%EB%92%B7%EC%A0%95%EB%A6%AC-%ED%95%98%EA%B8%B0
'React' 카테고리의 다른 글
- Total
- Today
- Yesterday
- JS
- 아티클
- 강의
- 프론트엔드
- html
- JavaScript
- 공부
- 기초
- 제로초
- 제이쿼리
- TypeScript
- VUE
- jQuery
- Article
- 뷰
- 차이
- 프로젝트
- React
- 자바스크립트
- 브라우저
- 리액트
- Study
- 코딩애플
- vue.js
- 타입스크립트
- Method
- 통신
- 메서드
- css
- frontend
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |