이번 편에서는 React Portals을 소개하고 작성하는 방법을 알아보겠습니다.
React Portals는 React에서의 특수한 개념이므로 다른 기술 혹은 분야에서의 Portal과는 의미가 다를 수 있습니다.
React의 고급 기능이므로 React를 익히는 목적이라면 어떤 것을 위한 기능인지 목적만 확인하는 정도로 충분합니다.
<!-- /public/index.html -->
<body>
<div id='root'></div>
</body>
import React from 'react'
import ReactDOM from 'react-dom'
const App = () => (
<div>App</div>
)
ReactDOM.render(<App />, document.getElementById('root'))
지금까지 소개한 내용으로 볼 떄에 React는 위 예시와 같이 최상위 태그 아래에 모든 컴포넌트를 '집어넣는' 구조입니다.
로그인창, 결제창 등을 위한 다이얼로그와 같은 컴포넌트를 작성하다 보면 이러한 구조를 벗어나 또 다른 최상위 태그에 위치하게 하고 싶을 때가 있습니다.
<!-- /public/index.html -->
<body>
<div id='root'></div>
<div id='other'></div>
</body>
import React from 'react'
import ReactDOM from 'react-dom'
const App = () => (
<div>App</div>
)
ReactDOM.render(<App />, document.getElementById('root'))
const Other = () => (
<div>Other</div>
)
ReactDOM.render(<Other />, document.getElementById('other'))
이런 형태를 상상해볼 수 있지만 App 컴포넌트와 Other 컴포넌트 사이에 State를 공유하는 등 복잡한 동작이 늘어날수록 점점 구조가 이상하게 변합니다.
더 나은 방법이 필요한 순간입니다.
<!-- /public/index.html -->
<body>
<div id='root'></div>
<div id='other'></div>
</body>
import React from 'react'
import ReactDOM, { createPortal } from 'react-dom'
const App = () => (
<>
<div>App</div>
<Other />
</>
)
const Other = () => {
return createPortal(<div>Other</div>, document.getElementById('other'))
}
ReactDOM.render(<App />, document.getElementById('root'))
React Portals를 이용하면 위에서 언급한 요구사항을 우회책 없이도 해결할 수 있습니다.
익숙하지 않으므로 하나씩 살펴보겠습니다.
React 라이브러리에서 createPortal을 불러오기.
App 컴포넌트에 Other 컴포넌트 포함하기.
Other 컴포넌트 안에서 createPortal 함수를 실행한 값을 return 하기.
해당 컴포넌트의 아래가 아니라 다른 태그의 하위에 위치한다는 것이 처음에는 헷갈릴 수 있지만 금방 적응할 수 있습니다.
import React, { useMemo } from 'react'
import ReactDOM, { createPortal } from 'react-dom'
const App = () => (
<>
<div>App</div>
<Other />
</>
)
const Other = () => {
const rootElement = useMemo(() => document.getElementById('other'))
return createPortal(<div>Other</div>, rootElement)
}
ReactDOM.render(<App />, document.getElementById('root'))
상태가 바뀌면 컴포넌트를 다시 그리는데, 그 때 마다 document.getElementById 메서드를 사용하는 것은 효율이 좋지 않으므로 React Hooks의 useMemo 함수를 이용하여 간단하나마 최적화를 해주는 것이 좋습니다.
<!-- /public/index.html -->
<body>
<div id='root'></div>
<div id='other'></div>
</body>
import React, { useMemo } from 'react'
import ReactDOM, { createPortal } from 'react-dom'
const App = () => (
<>
<div>App</div>
<Portal>
<Other />
</Portal>
</>
)
const Portal = ({ children }) => {
const rootElement = useMemo(() => document.getElementById('other'))
return createPortal(children, rootElement)
}
const Other = () => (
<div>Other</div>
)
ReactDOM.render(<App />, document.getElementById('root'))
위 예시처럼 Portal 컴포넌트로 분리하면 보다 더 읽기 편하게 작성할 수 있습니다.
좋아요와 댓글 감사합니다.
오탈자, 질문 등은 언제든지 댓글로 달아주세요!