ReactDOM.createPortal

Geonil Jang·2021년 3월 2일
0
post-thumbnail

ReactDOM

디아블로 생각 나는 portal

1) 리액트 돔 api중에 createPortal 사용해보기

modal을 사용하기 위해서는 redux를 연결하거나 contextAPI를 사용해서
modal provider를 만들어 주고는 했다. 그런데 보니깐 리액트 돔 api중에 하나인 createPortal을 이용해서 모달을 만들어 사용할 수 있다는 부분을 보고는 나도 한 번 만들어 보고 싶다는 생각이 무작정 들어서 사용해보았다.

사용1)

다음과 같이 사용했을 경우에는 조각조각 나있는 파편 같아 결합되어 있다는 느낌을 들지 않았다.
일단 사용해보자를 목표로 했기 때문에 사용할 일이 있으면 조금씩 수정을 해봐야겠다.

//collection-item.component.tsx
import React from "react";
import "./collection-item.styles.scss";

import Modal, { useModal } from "components/modal/modal.component";

type CollectionItem = {
    id:number|string;
    name:string;
    price:string | number;
    imageUrl:string
}

const CollectionItem = ( { id, name, price, imageUrl } :CollectionItem ) => {
  const { open, modalControl } = useModal();


  const handleOpenModal = () => {
    modalControl.open();
  };
  const handleClose = () => {
    modalControl.close();
  };

  return (
    <>
      <div className="collection-item" onClick={handleOpenModal}>
        <div className="image"
          style={{
            backgroundImage:`url(${imageUrl})`
          }}
        />
        <div className="collection-footer">
          <span className="name">{name}</span>
          <span className="price">{price}</span>
        </div>
      </div>
      {open && (
        <Modal onClose={handleClose}>
          <div className="collection-footer" style={{ color:'white' }}>
            <span className="name">{name}</span>
            <span className="price">{price}</span>
          </div>
        </Modal>
      )}
    </>
  );
};

export default CollectionItem;

사용2)

//modal.component.tsx
import React, { useEffect, useState, useCallback } from "react";
import ReactDOM from "react-dom";
import "./modal.styles.scss";


const Modal = ( { children, onClose }:any ) => {

  const modalRoot = document.querySelector( "#modal" );

  if( !modalRoot ){
    throw new Error( "Can't use modal component without root modal container" );
  }

  const handleRemoveModal = () => {    
    onClose();
    requestAnimationFrame( ()=>{
      modalRoot.classList.remove( "open" );
      modalRoot.innerHTML = ``;
      document.body.style.overflow = "";
    } );
    
  };

  useEffect( ()=>{
    document.body.style.overflow = "hidden";
    modalRoot.classList.add( "open" );
  }, [] );
  
  return (
    <ModalContainer>
      <button className="close" onClick={handleRemoveModal}></button>
      {children}
    </ModalContainer>
  );
};


const ModalContainer = ( { children }:any ) =>{
  const modalRoot = document.querySelector( "#modal" );

  if( !modalRoot ){
    throw new Error( "Can't use modal component without root modal continer by id modal" );
  }

  return ReactDOM.createPortal( children, modalRoot );
};

export const useModal = () => {
  const [ open, setOpen ] = useState( false );
  return {
    open,
    modalControl:{
      close:useCallback( ()=>{setOpen( false );}, [ open ] ),
      open:useCallback( ()=>{setOpen( true );}, [ open ] )
    }
  };
};



export default Modal;
profile
takeaways

0개의 댓글