Modal 여러개 띄우기

issol·2023년 4월 16일
0

Next

목록 보기
2/3

AS IS

기존 모달 사용 방식은 Context안에 children을 보내서 createPortal로 띄우는 방식이었다. 그런데 이 방식은 모달이 하나 뜨면 다른 모달이 꺼져버린다는 것이 문제가 되었다. 모달 위에 확인 모달을 띄워야 할 때가 있는데 이 경우에는 dom 구조 바깥에 뜨는 modal의 z-index를 조정한다고 해결이 되지 않아서 변경하기로 마음을 먹었다.

//ModalProvider.js

export default function ModalProvider({ children, selector }: Props) {
  const ref = useRef<any>(null)
  const [mounted, setMounted] = useState(false)
  const [modal, setModal] = useState<ReactNode>(null)

  useEffect(() => {
    ref.current = document.getElementById(selector)
    setMounted(true)
  }, [selector])

  const handleClickOverlay = (event: React.MouseEvent<HTMLDivElement>) => {
    if (event.currentTarget !== event.target) return

    setModal(null)
  }

  const renderModal = () => {
    return modal ? (
      <Overlay
        onClick={e => {
          if (clickable) handleClickOverlay(e)
          return
        }}
        role='presentation'
      >
        {modal}
      </Overlay>
    ) : null
  }

  return (
    <ModalContext.Provider value={{ setModal, setClickable, setScrollable }}>
      {mounted ? createPortal(renderModal(), ref.current) : null}
      {children}
    </ModalContext.Provider>
  )
}

TO BE

1. context에서 redux로 전환

  • 이미 사용하고 있는 redux이기도 하고 따로 context를 사용해서 modal만의 state를 관리하는건 불필요하다고 생각했다.
  • 또한 여러 모달을 띄울 수 있게 할 예정이기 때문에 추적과 관리가 용이할 수 있으면 좋을 것 같았다.
  • context는 상태 변경 감지를 위해 다시 렌더링되기 때문에 맘에 안들었다.
import { createSlice } from '@reduxjs/toolkit'
import { ReactNode } from 'react'

export type ModalType = {
  type: string
  children: ReactNode
  isCloseable?: boolean
}
const initialState: Array<ModalType> = []

export const modalSlice = createSlice({
  name: 'modal',
  initialState,
  reducers: {
    openModal: (state, action) => {
     //type은 모달을 구분 짓는 이름, children은 띄울 modal 
      const { type, children } = action.payload
     
      return state.concat({ type, children })
    },
    closeModal: (state, action) => {
      state.pop()
    },
  },
})

export const { openModal, closeModal } = modalSlice.actions
export default modalSlice.reducer


모달이 쌓이면 이런식으로 나오게 된다.

2. hook으로 만들기

그리고 이걸 hook으로 만들어서 어디서나 편하게 가져다 쓸 수 있게 만들었다.

import { ModalType, closeModal, openModal } from '@src/store/modal'
import { useAppDispatch } from './useRedux'

function useModal() {
  const dispatch = useAppDispatch()

  const handleOpenModal = ({ type, children, isCloseable }: ModalType) => {
    dispatch(openModal({ type, children, isCloseable }))
  }

  const handleCloseModal = (type: string) => {
    dispatch(closeModal(type))
  }

  return { openModal: handleOpenModal, closeModal: handleCloseModal }
}

export default useModal

3. 간편하게 사용하기

 openModal({ type: '' , children : <Modal /> }) 
 //type에는 unique한 이름, children에는 띄우고 싶은 컴포넌트 
 
 closeModal('')
 //openMdoal에서 type에 적는 unique한 이름 


이렇게 모달 위에 모달이 잘 뜨게 된다!

참고 : https://velog.io/@rkio/React-ReactDOM.createPortal
https://ko.reactjs.org/docs/portals.html
https://jeonghwan-kim.github.io/2022/06/02/react-portal

profile
프론트 엔드 개발자

0개의 댓글