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" ]
}