Portal 이용해서 react에서 Redux없이 Modal창 만들기 도전

둘둘·2020년 5월 3일
3

정말 오랜만의 벨로그 포스팅. 블로그를 어떻게 해야 할지 많은 고민 끝에..
github 블로그는 공부한거 정리용, 벨로그는 확실한 내용 정리용으로 쓰기로 마음먹었다.
github에 있던 글들은 정리에 힘쓴 확실한 글 말곤 안 옮기기로 마음 먹었다.
(사실 지난번에도 옮기다가 현타가 와서 몇 달동안 블로그를 못 썼다..)


여튼 오랜만의 포스팅 시작!

주의! 제가 쓴 이 방식이 구조상 좋은 방식은 아닙니다. 나중에 redux 공부하고 다시 코드 엎을 예정입니다.

포탈하면 제일 먼저 떠오르는 이미지.. 요런 통로쓰..!
내가 생각했던 진짜 포탈의 역할을 하는듯?! 여튼...

React Portal

Portal을 이용하면 react에서 부모 컴포넌트의 DOM 계층구조 바깥의 DOM node에 children을 랜더할 수 있다고 한다. 해석이 맞는진 모르겠으나 자세한건 공식문서 고고!

일단 "modal"을 심어줘야 해서 index.html에 심어줄게 있다

<div id="modal"></div>

나는 modal div를 "root" 바로 아래에다가 심어놨다.
왜냐.. 공식문서에서도 그렇게 해서 ㅋㅋ

그 다음엔 Modal 폴더를 만들고, Portal.js와 index.js 파일을 만들어준다.

// Portal.js
import ReactDOM from "react-dom";

const Portal = ({ children }) => {
  const el = document.getElementById("modal");
  return ReactDOM.createPortal(children, el);
};

export default Portal;

그 담엔 Modal폴더 안의 index.js를 채워줄 차례..!

import React, { useState, useEffect } from "react";
import { withRouter } from "react-router-dom";
import axios from "axios";
import "./modal.scss";

const Modal = props => {
  const [open, setOpen] = useState(false);

  useEffect(() => {
    setOpen(props.open);
  }, [props.open]);

  const handleModal = flag => {
    setOpen(flag);
    props.onClose && props.onClose();
  };

  const [applicantValues, setApplicantValues] = useState({
    clientName: "",
    phoneNumber: ""
  });

  const handleChange = e => {
    const { name, value } = e.target;
    setApplicantValues({ ...applicantValues, [name]: value });
  };
  
const handleSubmit = e => {

handleModal(false);

// axios로 정보 보내는 과정은 중략

}
  
  return (
    <>
      <div
        className="modal-background"
        onClick={() => {
          handleModal(false);
          // dim부분을 눌렀을때도 modal창이 꺼질 수 있도록 하는 장치!
        }}
      ></div>
      <div className="modal-container">
      // 중략된 부분에 x 버튼에도 modal창이 꺼질 수 있도록 위와 같은 handleModal(false)코드 달아주기
 /// 중략...
 	  </div>
    </>
  )
 }

중간중간 많은 코드들을 생략했다 ㅋㅋ
Hooks는 아직 익숙지 않은데 modal창 만들면서 열심히 구글링해서 적용해봤지만..
아직도 어색어색... 시간 있을때 다시 정리해야지 ㅠㅠ

Modal 폴더 안의 index.js는 크게 modal-background라는 div와 modal-container라는 div로 구성했다.

modal-background는 dim이 되는 부분이기 때문에 백그라운드 컬러를 잘 조정해야 한다.

.modal-background {
  background: rgba(0, 0, 0, 0.55);
  position: fixed;
  display: flex;
  display: -ms-flexbox;
  // 중략
}

modal-container를 반응형으로 만들때가 세상 귀찮았던 작업이다.
폰에서 확인하고, 그램에서 확인하고..

그래도 만들어놓고 여기 저기에서 잘 뜨면 뿌듯한 맛에 프론트 하지..
나는 역시 답정너 프론트엔드인가 부다~

요 개발자 성향 테스트 해보고 싶은 분들은 bftest <- 클릭 궈궈!


다시 본론으로 돌아와서..ㅋㅋㅋㅋ

내가 modal을 실행시켜야 하는 곳이 특정 버튼을 눌렀을 시점이여서
그냥 그 버튼에다가 모달을 붙여버렸다!

이래서 구조상 좋지 않다고 한 것이다..!

요렇게...

import React, { Component } from "react";
import { withRouter } from "react-router-dom";
import Modal from "Components/Modal";
import Portal from "Components/Modal/Portal";

class ApplyBtn extends Component {
  constructor() {
    super();
    this.state = { modalOpen: false };
  }

  openModal = () => {
    this.setState({ modalOpen: true });
  };

  closeModal = () => {
    this.setState({ modalOpen: false });
  };

  handleClickConsult = () => {
    this.openModal();
    // 중략
  };

  render() {
    const { modalOpen } = this.state;
    return (
      <>
        <div className={this.props.className} onClick={this.handleClickConsult}>
          신청
        </div>
        {modalOpen && (
          <Portal>
            <Modal
              open={modalOpen}
              onClose={() => {
                this.closeModal();
              }}
            />
          </Portal>
        )}
      </>
    );
  }
}

export default withRouter(ApplyBtn);

짠 이제 신청 버튼을 누르면 modal div에 내가 만든 Modal이 요렇게 뜬다!

이렇게 보면 꽤 괜찮은 방법같기도 하나..
원래 내가 구현해야 했던 구조는 modal이 전체 페이지를 감싸는 형태가 되어야 했기 때문에
redux를 쓰지 않으면 좀 힘들것 같다.. 이덕수씨랑은 언제 친해질까 핰...

다른 방법이 있다면 알려주세요 고수님덜... 😂😂

그래도 맨 처음 modal을 만들었을 땐 Portal 같은걸로 적용 안하고
그냥 컴포넌트 형태로 모양만 modal처럼 띄울수 있는 걸 만들었는데 나름 발전했다ㅋㅋㅋㅋ
담번에 리덕스로 다시 도전해볼 날이 오겠쥐!!

포스팅을 마치기 전!! 나만 신기할 수도 있지만 이날 새로 배운 것 하나 투척!

index.js에다가 요걸 심는다

sessionStorage.setItem("ref", document.referrer);

요 referrer를 통해 직전 경로를 파악할 수 있다.
나는 요걸 Modal 폴더의 index.js에서 제출하는 함수는 handleSubmit 함수에 referrer를 심었다.

applicantValues.referrer = sessionStorage.getItem("ref");

세션 스토리지를 요렇게 활용할 수도 있다니 신기씐기!!

profile
Dooreplay! 안 되면 될 때까지,

8개의 댓글

comment-user-thumbnail
2020년 6월 4일

오호 이거 흥미롭네요 ㅋㅋㅋ
나중에 저도 도전해보고 과연 다른 컴포넌트 렌더에 어떤 영향을 줄지 매우 궁금쓰

2개의 답글
comment-user-thumbnail
2020년 6월 14일

오오 하면서 내리다가 깜짝 놀랐네요...베프테스트가 왜 여기서 나와? 라면서 보니깤ㅋㅋㅋ두리님 블로그! 모달창 만들기 저도 한번 도전 해보겠어요!!!

1개의 답글
comment-user-thumbnail
2020년 9월 6일

저는 Redux 방법으로 구현해봤는데, 확실히 편한건 Portal을 이용해서 하는 것이 편해보이네요!

답글 달기
comment-user-thumbnail
2021년 2월 5일

최근에 Redux가 하락세인거 같은데, 꼭 Redux를 적용해야하나 이생각이 드네요.... 지금 모달 라이브러리 없이 구현하는데 잘 보고 갑니다.

답글 달기
comment-user-thumbnail
2023년 4월 10일

Good post, very well written. https://tellpopeyes.net/

답글 달기