permalinkuseRenderInPortal
This hook will find an HTMLElement in the DOM and render the given JSX as its child. Portals are useful for layering UI elements on top (popovers, modals, toasts, tooltips, etc).
Use it like this:
src/MyComponent.purs
module MyComponent where
import Prelude
import React.Basic.Hooks (Component, component)
import Hooks.UseRenderInPortal (useRenderInPortal)
import React.Basic.Hooks as React
import React.Basic.DOM (text, div_) as R
mkComponent ∷ Component {}
mkComponent = component "MyComponent" \_ -> React.do
renderInPortal ← useRenderInPortal "my-portal"
pure $ renderInPortal $ R.div_ [ R.text "Hello Portal" ]
src/Hooks/UseRenderInPortal.purs
module Hooks.UseRenderInPortal
( UseRenderInPortal(..)
, useRenderInPortal
) where
import Prelude
import Data.Maybe (Maybe, Maybe(..), isNothing)
import Data.Newtype (class Newtype)
import React.Basic.DOM (createPortal)
import React.Basic.Hooks as React
import Web.DOM (Element)
import Web.HTML (window)
import Web.HTML.Window (document)
import Web.HTML.HTMLDocument (toNonElementParentNode) as HTMLDocument
import Web.DOM.NonElementParentNode (getElementById)
import React.Basic.Hooks (UseEffect, UseState, useEffectOnce)
import React.Basic.Hooks.Internal (Hook, coerceHook)
import Data.Foldable (foldMap)
import Effect (Effect)
newtype UseRenderInPortal hooks = UseRenderInPortal
(UseEffect Unit (UseState (Maybe Element) hooks))
derive instance Newtype (UseRenderInPortal hooks) _
useRenderInPortal ∷ String → Hook UseRenderInPortal (JSX → JSX)
useRenderInPortal portalId = coerceHook React.do
portalOrNot /\ setPortal ← React.useState' Nothing
let renderInPortal jsx = portalOrNot # foldMap (createPortal jsx)
useEffectOnce do
when (portalOrNot # isNothing) do
findElementByIdInDocument portalId >>= setPortal
mempty
pure renderInPortal
findElementByIdInDocument ∷ String → Effect (Maybe Element)
findElementByIdInDocument id = do
doc ← window >>= document
let node = HTMLDocument.toNonElementParentNode doc
getElementById id node