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

 

 

+ Recent posts