Skip to content

Instantly share code, notes, and snippets.

@thomashoneyman
Created June 18, 2020 01:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thomashoneyman/5d21035dabf1512be120dba444411b56 to your computer and use it in GitHub Desktop.
Save thomashoneyman/5d21035dabf1512be120dba444411b56 to your computer and use it in GitHub Desktop.
Pass callback to Halogen child
module Main where
import Prelude
import Data.Foldable (for_)
import Data.Maybe (Maybe(..))
import Data.Symbol (SProxy(..))
import Effect (Effect)
import Effect.AVar as AVar
import Effect.Aff (Aff)
import Effect.Aff.Class (class MonadAff, liftAff)
import Halogen (liftEffect)
import Halogen as H
import Halogen.Aff as HA
import Halogen.HTML as HH
import Halogen.HTML.Events as HE
import Halogen.Query.EventSource (affEventSource)
import Halogen.Query.EventSource as EventSource
import Halogen.VDom.Driver (runUI)
main :: Effect Unit
main = HA.runHalogenAff do
body <- HA.awaitBody
runUI parent unit body
-- Create an `Aff` function that can be used to trigger an action in one component
-- to be evaluated in another.
mkCallback :: forall st act ps o m. MonadAff m => act -> H.HalogenM st act ps o m (Maybe (Aff Unit))
mkCallback f = do
cbAVar <- liftEffect AVar.empty
_ <- H.subscribe $ affEventSource \emitter -> do
let callback = EventSource.emit emitter f
pure mempty <* liftEffect (AVar.tryPut callback cbAVar)
liftEffect $ AVar.tryTake cbAVar
-- The parent is able to evaluate actions triggered in the child component by
-- passing down a callback
type ParentState = { count :: Int, cb :: Aff Unit }
type Slots = ( child :: forall q. H.Slot q Void Unit )
_child = SProxy :: SProxy "child"
data ParentAction
= Initialize
| Evaluate (forall o m. H.HalogenM ParentState ParentAction Slots o m Unit)
parent :: forall q i o m. MonadAff m => H.Component HH.HTML q i o m
parent =
H.mkComponent
{ initialState: \_ -> { count: 0, cb: pure unit }
, render
, eval: H.mkEval $ H.defaultEval
{ handleAction = handleAction
, initialize = Just Initialize
}
}
where
render state =
HH.div_
[ HH.p_ [ HH.text $ "You clicked " <> show state.count <> " times" ]
, HH.slot _child unit child { cb: state.cb } absurd
]
handleAction :: ParentAction -> H.HalogenM ParentState ParentAction Slots o m Unit
handleAction = case _ of
Initialize -> do
-- make the callback function
mbCallback <- mkCallback $ Evaluate do
H.modify_ \st -> st { count = st.count + 1 }
-- set it in state so it can be passed to child components
for_ mbCallback \cb -> H.modify_ _ { cb = cb }
Evaluate act -> act
-- The child component is able to trigger actions in its parent without sending
-- a message, by receiving a callback passed down.
type ChildInput = { cb :: Aff Unit }
data ChildAction = Run (Aff Unit) | Receive ChildInput
child :: forall q o m. MonadAff m => H.Component HH.HTML q ChildInput o m
child =
H.mkComponent
{ initialState: identity
, render
, eval: H.mkEval $ H.defaultEval
{ handleAction = handleAction
, receive = Just <<< Receive
}
}
where
render state =
HH.div_
[ HH.button
[ HE.onClick \_ -> Just (Run state.cb) ]
[ HH.text "Click me (child)" ]
]
handleAction = case _ of
Receive i -> H.put i
Run cb -> liftAff cb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment