내일배움캠프

230705 Custom hooks으로 modal 만들기

Neda 2023. 7. 5. 20:15

230705 Custom hooks으로 modal 만들기

 

Modal의 불편함

Modal을 열고 닫는 상태 관리를 위한 useState의 사용

return 문안에서 조건 연산자를 사용한 Modal 렌더링

'확인', '취소' 버튼을 눌렀을 때 실행할 함수를 각각 props로 넘겨야 한다

function App() {
  const [isOpenModal, setOpenModal] = useState(false);
  
  return (
    <div className="App">
        <Button onClick={() => setOpenModal(true)}>medium 버튼</Button>
        {isOpenModal && createPortal(
          <Modal
            type="confirm"
            closeFunc={() => setOpenModal(false)}
            confirmFunc={() => console.log("Confirmed")}
          >
            버튼이 2개인 모달
          </Modal>,document.body
        )}
    </div>
  );
}

 

usePopup의 장점

 return문에서 Modal을 매번 import하고 props를 넘길 필요가 없어 재사용이 쉽고, 코드가 간결해진다.

function App() {
  const [openPopup] = usePopup()
  
  const handleOpenModal = () => {
    const confirm = await openPopup({
      title:'모달',
      contents: `모달 내용`
    })
    if(!confirm) return // 취소 버튼 클릭
    console.log("Confirmed") // 확인 버튼 클릭
  }
  
  return (
    <div className="App">
        <Button onClick={handleOpenModal}>medium 버튼</Button>
    </div>
  );
}

 

usePopup 구현

createRoot를 사용하여 루트를 만들고 render 함수를 사용하여 렌더링

팝업이 닫힐 때는 unmount를 사용

Create-react-app에서 document.getElementById('root') 에 <App/>을 렌더링하듯이

id='popupContainer'인 div를 root로 만들어서 내부에 팝업 컴포넌트를 렌더링

https://react.dev/reference/react-dom/client/createRoot

 

createRoot – React

The library for web and native user interfaces

react.dev

const usePopup = () => {

  const createContainer = useCallback(() => {
    const container = document.getElementById("popupContainer")
    if(container) return container
    const containerDiv = document.createElement('div')
    containerDiv.id = 'popupContainer'
    document.body.appendChild(containerDiv)
    return containerDiv
  },[])

  const openPopup = useCallback(({ title, contents }) => {
    const root = createRoot(createContainer());
    return new Promise((resolve) => {

      const handleConfirm = () => {
        root.unmount()
        resolve(true);
        
      };

      const handleCancel = () => {
        root.unmount()
        resolve(false);

      };

      root.render(
        <div>
          <div className="popup-bg" onClick={handleCancel}></div>
          <div className="popup">
          <h2>{title}</h2>
          <div>{contents}</div>
          <div className="popup-btns">
          <button onClick={handleConfirm}>확인</button>
          <button onClick={handleCancel}>취소</button>
          </div>
          </div>
        </div>)
    });
  },[createContainer]);

  return [openPopup];
};