RactDOM portal
Portal機能は、親コンポーネントのDOM階層構造の外側にあるDOMノードに子をレンダリングする機能を提供します。 普通は自分を囲んでいる親の下で描かれます。しかし、視覚的に子を飛び出して出るように見せなければならない場合があります。Portal 使用前
import React from 'react'
import { useState } from 'react'
export default function Dialog(props) {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button
style={{
position: 'absolute',
left: 100,
}}
onClick={()=> setIsOpen(true)}>Open</button>
{isOpen && <div
style={{
position: "absolute",
zIndex: 99,
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
border: "1px solid black",
padding: 24,
backgroundColor: "white",
}}
>
{ typeof props.title === "string" ? (
<h1>{props.title}</h1>
) : (props.title)}
{ typeof props.description === "string" ? (
<h1>{props.description}</h1>
) : (props.description)}
{ typeof props.button === "string" ? (
<button style={{backgroundColor:"red", color: "wthie"}}onClick={()=> setIsOpen(false)}>{props.button}</button>
) : (
<div onClick={()=>setIsOpen(false)}>
{props.button}
</div>
)}
</div>
}
{isOpen && <div style={{
position: "fixed",
top: 0,
left: 0,
bottom: 0,
right: 0,
backgroundColor: "lightgray"
}}/>}
</>
);
}
Dialog.jsxは isOpen State値によって、ボタンの表示状態が変わるし、
Propsを受け取って、dialogに出力する内容を決めます。
import React from 'react'
import Dialog from './Dialog'
export default function ThankyouDialog() {
return (
<Dialog
title={<h1 style={{color: `red`}}>Sorry</h1>}
description="Sorry My Fault!!"
button={<button style={{backgroundColor: "red", color:"white"}}>close</button>}
/>
)
}
ThankyouDialog.jsxはDialog.jsxにDialogに出力する内容をPropsにて渡します。
import React from 'react'
import { createPortal } from 'react-dom'
import ThankyouDialog from './ThankyouDialog'
export default function Example() {
return (
<div>
<ThankyouDialog />
<div style={{ position: "absolute "}}>
<button>hahah</button>
</div>
</div>
)
}
Example.jsxはThankyouDialogコンポーネントとhahahaボタンを出力しています。
下は、openボタンをクリックした画面です。
ダイアログがz-index基準で既存のものをすべて上書きしなければならないが、後ろにあるものが上位を占めるためhahaが見えてしまいます。
hahahaを非表示にするには、Example.jsxのThankyouDialogをhahaha Divの次に配置させれば、hahahaボタンが非表示になります。
上記の場合は、子供たちが同じ親の下にいるため、位置を変えることで被せることができますが、
もしレイヤーがあるコンポーネントの中に中に入っていると、レイヤーが順次持っているUIスタックを離れることが不可能になります。
Portal 使用方法
index.htmlにidをportalを持つdivを追加します。
// 省略
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="portal"></div>
// 省略
</body>
</html>
import React from 'react'
import { createPortal } from 'react-dom'
import ThankyouDialog from './ThankyouDialog'
const Portal = (props) => {
return createPortal(props.children, document.getElementById('portal'));
}
export default function Example() {
return (
<div>
<Portal>
<ThankyouDialog />
</Portal>
<div style={{ position: "absolute "}}>
<button>hahah</button>
</div>
</div>
)
}
createPortal(child, container)を使って、外側にあるDOMノードに子をレンダリングします。
上記だと、が子ともになり、index.htmlのportal divがcontainerになります。
開発者モードで確認したElement!ThankyouDialogがportal divにレンダリングされていることがわかります!
portal event
import React from 'react'
import { createPortal } from 'react-dom'
import ThankyouDialog from './ThankyouDialog'
const Portal = (props) => {
return createPortal(props.children, document.getElementById('portal'));
}
export default function Example() {
return (
<div onClick={() => console.log('div')}>
<Portal>
<ThankyouDialog />
</Portal>
<div style={{ position: "absolute "}}>
<button>hahah</button>
</div>
</div>
)
}
divにeventとしてonClickを定義しています。
hahahaを押下すると、もちろんdivログが出力されますが、ThankyouDialogはindex.htmlのrootではない、portalタグに入っているため、portalでEventが起きてもrootには伝わると思われますが
Reactでは意図的にPortalにあるElementでもそこで発生するEventはrootにも伝えています。