For simplicity, suppose I have a parentComponent
with a label with text “foo” and a childComponent
that renders a button clicking which should make parentComponent
change “foo” to “bar”.
In raw JS it’s done by obtaining a setText
via hook call _ /\ setText <- useState "foo"
in the parent, then passing the setText
down to the child, so it can call it when button is clicked.
Sounds simple. But the problem is that instantiating childComponent
is “effectful”, and so has to be done outside the React.do
block. But setText
first appears inside the React.do
, where I can’t seem to be able to do "Effect"ful actions!
So how do I do that?
Here’s a code for the example:
module Main where
import Prelude
import Data.Maybe (Maybe(..))
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Exception (throw)
import React.Basic.DOM as R
import React.Basic.DOM.Client (createRoot, renderRoot)
import React.Basic.Events (handler_)
import React.Basic.Hooks (Component, component, useState')
import React.Basic.Hooks as React
import Web.HTML.HTMLDocument (toNonElementParentNode)
import Web.DOM.NonElementParentNode (getElementById)
import Web.HTML (window)
import Web.HTML.Window (document)
main :: Effect Unit
main = do
doc <- document =<< window
root <- getElementById "root" $ toNonElementParentNode doc
case root of
Nothing -> throw "Could not find root."
Just container -> do
reactRoot <- createRoot container
app <- parentComponent
renderRoot reactRoot (app {})
parentComponent :: Component {}
parentComponent = do
component "Parent" \_ -> React.do
text /\ setText <- useState' "foo"
ch <- childComponent setText -- ERROR: can't do Effect, neither can move outside as `setText` didn't exist there
pure $ R.div
{ children: [ R.label { children: [ R.text text ] }
, ch ] }
childComponent :: (String -> Effect Unit) -> Component {}
childComponent setText = component "Child" \_ -> React.do
pure $ R.button
{ onClick: handler_ $ setText "bar"
, children: [ R.text "Change Text to bar" ]
}