«   2024/09   »
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
Archives
Today
Total
Recent Posts
Recent Comments
관리 메뉴

뉴히의 개발 로그

[TIL] 20230623 - React onclick/onblur 이벤트 동시에 일어나면? 리액트로 모달 만들기 (배경 클릭시 닫힘 stopPropagation 메서드/React portal) 본문

개발일지/TIL

[TIL] 20230623 - React onclick/onblur 이벤트 동시에 일어나면? 리액트로 모달 만들기 (배경 클릭시 닫힘 stopPropagation 메서드/React portal)

뉴히 2023. 6. 23. 20:16

 Select - onClick, onBlur 이벤트 

셀렉트 박스에 onclick으로 타겟의 텍스트로 상단 텍스트가 바뀌도록 코딩해뒀다.

     
     const [buttonText, setbuttonText] = useState('리액트');
          const changeText = (e) => {
          const textValue = e.target.textContent;
          setbuttonText(textValue);
          setSlide((slide) => !slide);
     };
 
 
     return (
           
                <div>
                    <SelectBtn onClick={slideToggleOne} onBlur={() => setSlide(false)} className="selDefault" >
                        {buttonText}
                    </SelectBtn>
                    <Dropdown className={slide ? 'slideShow' : 'slideHide'} leftLocation={'8px'}>
                        <li>
                            <DropOption onClick={changeText}>리액트</DropOption>
                        </li>
                        <li>
                            <DropOption onClick={changeText}>자바</DropOption>
                        </li>
                        <li>
                            <DropOption onClick={changeText}>스프링</DropOption>
                        </li>
                        <li>
                            <DropOption onClick={changeText}>리액트네이티브</DropOption>
                        </li>
                    </Dropdown>
                </div>
     )
 

코드를 왜 이모양이르 짯는지 몰라도 ^^ 어제 잠결에인가 ㅎㅎㅎ

onBlur를 추가하니 셀렉트 옵션들을 두번을 클릭해야만 옵션들이 닫히는거다

 

해결 방법은 두가지가 있다.

 

1) useState 코드 간결하게

 
const [buttonText, setbuttonText] = useState('리액트');
    const changeText = (e) => {
        const textValue = e.target.textContent;
        setbuttonText(textValue);
        setSlide((slide) => !slide);
    }
 
====================>
 
const [buttonText, setbuttonText] = useState('리액트');
    const changeText = (e) => {
        setbuttonText(e.target.textContent);
    };
 

 

onclick만 사용했을땐 별 문제 없었는데 onBlur를 같이 사용하니 두번클릭을 해야했는데 setSlide를 두번해줘서인지....

무튼 해결된다!

 

2) 이벤트 핸들러 작동 순서에 따라

onBlur --> onClick

onClikc보다 onBlur가 먼저 실행된다.

onMouseDown --> onBlur --> onClick

onMouseDown은 onBlur보다 먼저 실행된다!

 
    return(
               <div>
                    <SelectBtn className="selDefault" onClick={slideToggleOne} onBlur={() => setSlide(false)}>
                        {buttonText}
                    </SelectBtn>
                    <Dropdown className={slide ? 'slideShow' : 'slideHide'} leftLocation={'8px'}>
                        <li>
                            <DropOption onMouseDown={changeText}>리액트</DropOption>
                        </li>
                        <li>
                            <DropOption onMouseDown={changeText}>자바</DropOption>
                        </li>
                        <li>
                            <DropOption onMouseDown={changeText}>스프링</DropOption>
                        </li>
                        <li>
                            <DropOption onMouseDown={changeText}>리액트네이티브</DropOption>
                        </li>
                    </Dropdown>
                </div>
)

onClick을 onMouseDown으로 바꿔줘도 해결된당!!!

 

 

 Modal 배경 클릭시 닫힘 이벤트 

 

만들어둔 모달 영역!

백그라운드 대크 안에 모달 컨텐츠 영역을 마크업해뒀었다

그런데 배경 클릭시 모달이 닫혀야하고 닫힘 버튼(X)을 눌러도 모달이 닫혀야한다 

const toggleModal = () => {
    setIsOpen((isOpen) => !isOpen);
};
    
return (  
    
     <div className={isOpenTwo ? 'show' : 'hide'}>
        <BgModal onClick={toggleModal}>
            <ModalBox>
                <CloseBtn onClick={toggleModal}>X</CloseBtn>
                <p>닫기 버튼 1개가 있고, 외부 영역을 누르면 모달이 닫혀요.</p>
            </ModalBox>
        </BgModal>
    </div>
    
)

 닫기 버튼 이벤트가 잘되었고 배경 누를시 닫기 이벤트를 넣어주니까

배경을 넣으면 닫히는데 버튼에 이벤트가 동작하지 않았고 ModalBox를 눌러도 닫히는 거당 ㅎㅎㅎㅎㅎㅎㅎㅎ

이는 이벤트 버블링때문인데

해결방법을 구글링 해본결과 JSX 구조를 조금 바꾸면 닫기버튼, 배경버튼 이벤트가 잘 동작되었다

// 구조변경 방법

<div className={isOpenTwo ? 'show' : 'hide'}>
    <BgModal onClick={toggleModal}></BgModal>
    <ModalBox>
        <CloseBtn onClick={toggleModal}>X</CloseBtn>
        <p>닫기 버튼 1개가 있고, 외부 영역을 누르면 모달이 닫혀요.</p>
    </ModalBox>
</div>

 

그런데 마침 동료분이 라이브코딩하며 딱 요것을 보여주셨다 !

구조를 변경하지 않아도 이벤트를 줘서 해결 가능하다

 

stopPropagation 이벤트 메소드 사용

 

<div className={isOpenTwo ? 'show' : 'hide'}>
    <BgModal onClick={toggleModalTwo}>
        <ModalBox
            onClick={(event) => {
                event.stopPropagation();
            }}>
            <CloseBtn onClick={toggleModalTwo}>X</CloseBtn>
            <p>닫기 버튼 1개가 있고, 외부 영역을 누르면 모달이 닫혀요.</p>
        </ModalBox>
    </BgModal>
</div>

해결 가능!!!

 

 

 리액트 Portal 

또하나의 모달 팁!

portal 사용하면 좋다!

리액트(SPA)의 경우는 root라는 id를 가진 tag 안에 내용들을 바꿔 껴가면서 구성하는 방식이다.

그래서 현재 모달은 root 태그 안에-안에-안에!! 위치한다 ㅎㅎㅎㅎ

그러하기 때문에 상위 요소들 때문에 모달이 가려질 수 있다.

z-index설정이 가능하긴 하지만 ! 

루트와 동일한(최상단)위치의 컴포넌트로 렌더링 할 수있도록 리액트에서 제공하는 기능

index.html  

portal-root 아이디를 가진 태그를 넣어두고

createPortal( 컴포넌트, DOM 엘리먼트)
    return createPortal(
        <div>
            <div className={isOpen ? 'show' : 'hide'}>
                <BgModal>
                    <ModalBox>
                        <p>닫기와 확인 버튼 2개가 있고, 외부 영역을 눌러도 모달이 닫히지 않아요.</p>
                        <div>
                            <StButton onClick={toggleModal} bgColor={'#ffb7a6'} fontColor={'#c72205'} acColor={'#ee6952'}>
                                닫기
                            </StButton>
                            <StButton bgColor={'#00e6bf'} acColor={'#00a589'}>
                                확인
                            </StButton>
                        </div>
                    </ModalBox>
                </BgModal>
            </div>         
        </div>,
        document.getElementById('portal-root')
    );

컴포넌트를 두번째 인자 안에 위치시켜라!

그럼 이렇게 모달이 portal-root 안에 들어가있당! ㅎㅎㅎ

모바일에서 뒤로가기 눌렀을때 이전 페이지가 아닌 모달만 닫히는 경우도

portal root를 쓰면 그렇다고...엇나 ?ㅎㅎㅎㅎㅎ

 

무튼 z-index로 해도 안될경우나 여러 귀찮은 상황을 방지하기위해 react portal을 써주면 좋다는거!