Being led by a hype, I had written some components in Halogen previously. Time has shown though “react-basic-hooks” to be a better choice, because so far it has been resulting in less code and is much easier to maintain/separate/reuse… That’s mainly due to the overhead and error-proneness Halogen causes whenever the question arises about communication between components.
So now I’ve got a mix of Halogen and React Hooks composed via <script>
together. Obviously it’s not a good situation, but rewriting the code from Halogen to React will take some time, which I don’t have. So I’m wondering, is it possible to integrate a “React Hooks” Component
to a “Halogen”-written DOM?
Example: in the code below there’s a Halogen page and a React Hooks label
. Can I integrate the latter to the former?
module Main where
import Prelude
import Effect (Effect)
import Halogen as H
import Halogen.Aff as HA
import Halogen.HTML as HH
import Halogen.VDom.Driver (runUI)
import React.Basic.Hooks (Component, component)
import React.Basic.DOM as R
main :: Effect Unit
main = HA.runHalogenAff do
body <- HA.awaitBody
runUI mainComponent unit body
mainComponent :: forall query input output m. H.Component query input output m
mainComponent =
H.mkComponent
{ initialState: const unit
, render
, eval: H.mkEval H.defaultEval
}
render :: forall m. Unit -> H.ComponentHTML Unit () m
render _ = HH.div_ []
helloLabel :: Component {}
helloLabel = component "Hello" \_ -> React.do
pure $ R.label { children: [R.text "hello world"] }
Idk how good of a solution it is, but I figured one way might be:
-
React: create kind of a “main function” for the component (the one one where you call createRoot
and renderRoot
), which then renders the component (helloLabel
in this case).
Now, this “main function” needs a location to hook into. Use special id for that, e.g. hellolabel
-
Halogen: add “Initialize” action.
-
Halogen: add an id (hellolabel
in this case) to where you’d want to render react component
-
Halogen: during the handling of “initialize action” call the react component “main function”
Implementation for the example code:
module Main where
import Prelude
import Data.Maybe (Maybe(..))
import Effect (Effect)
import Effect.Aff.Class (class MonadAff)
import Effect.Class.Console (log)
import Halogen as H
import Halogen.Aff as HA
import Halogen.HTML as HH
import Halogen.HTML.Properties as HP
import Halogen.VDom.Driver (runUI)
import React.Basic.DOM as R
import React.Basic.DOM.Client (createRoot, renderRoot)
import React.Basic.Hooks (Component, component)
import Web.DOM.NonElementParentNode (getElementById)
import Web.HTML (window)
import Web.HTML.HTMLDocument (toNonElementParentNode)
import Web.HTML.Window (document)
data Action = Initialize
main :: Effect Unit
main = HA.runHalogenAff do
body <- HA.awaitBody
runUI mainComponent unit body
mainComponent :: forall query m. MonadAff m => H.Component query Unit Unit m
mainComponent =
H.mkComponent
{ initialState: const unit
, render
, eval: H.mkEval H.defaultEval { handleAction = handleAction
, initialize = Just Initialize}
}
where
render :: Unit -> H.ComponentHTML Action () m
render _ = HH.div [HP.id "hellolabel"] []
handleAction :: Action -> H.HalogenM Unit Action () Unit m Unit
handleAction Initialize = H.liftEffect mainHelloLabel
-------- React component
mainHelloLabel :: Effect Unit
mainHelloLabel = do
doc <- document =<< window
root <- getElementById "hellolabel" $ toNonElementParentNode doc
case root of
Nothing -> log "Could not find root." *> pure unit
Just container -> do
reactRoot <- createRoot container
comp <- helloLabel
renderRoot reactRoot (comp {})
helloLabel :: Component {}
helloLabel = component "Hello" \_ -> React.do
pure $ R.label { children: [R.text "hello world"] }