Elmish.Hooks.Type
- Package
- purescript-elmish-hooks
- Repository
- collegevine/purescript-elmish-hooks
#Hook Source
newtype Hook a
The type of a hook, e.g. the result of calling useState
. It turns out
that hooks can be modeled as a continuation, where the callback function
returns a new component (created with wrapWithLocalState
) given the
encapsulated value. E.g., in the case of useState
, you can think of it as
accepting a callback function, which gets passed the current state and a
setter for the current state:
useState "" \(foo /\ setFoo) -> …
Modeling it as a continuation allows us to make it a monad and write in do-notation, which looks a lot like the React hooks syntax:
withHooks do
foo /\ setFoo <- useState ""
pure …
Instances
#mkHook Source
mkHook :: forall msg state a. ComponentName -> ((a -> ReactElement) -> ComponentDef msg state) -> Hook a
Given a ComponentName
and a function to create a ComponentDef
(from a
render function a -> ReactElement
), mkHook
creates a Hook a
. The name
can be anything, but it’s recommended to use
uniqueNameFromCurrentCallStack
to create a unique name based on where the
hook is called from in the stack trace. uniqueNameFromCurrentCallStack
accepts a skipFrames :: Int
argument to indicate how many frames back it
should look for the call site of the hook, as well as a prefix
argument
that will be the prefix of the resulting ComponentName
.
It’s also recommended to create the name in a where clause and make your hook a function accepting one argument. Even if your hook takes more than one argument, you can put the rest o the arguments in a lambda in the function body:
myHook x = \y z -> mkHook …
where
name = uniqueNameFromCurrentCallStack { skipFrames: 3, prefix: "MyHook" }
This ensures that the number of frames to skip is predictably 3. If
defining the function differently, a different number of frames can be
passed. uniqueNameFromCurrentCallStackTraced
can be used to help find the
correct number.
As an example of how to use mkHook
, useEffect
uses it like so:
useEffect :: Aff Unit -> Hook Unit
useEffect init =
mkHook name \render ->
{ init: forkVoid init
, update: \_ msg -> absurd msg
, view: \_ _ -> render unit
}
where
name = ComponentName $ genStableUUID { skipFrames: 3, prefix: "UseEffect" }
#uniqueNameFromCurrentCallStack Source
uniqueNameFromCurrentCallStack :: { prefix :: String, skipFrames :: Int } -> ComponentName
Generates a ComponentName
to be passed to mkHook
.
#uniqueNameFromCurrentCallStackTraced Source
uniqueNameFromCurrentCallStackTraced :: DebugWarning => { prefix :: String, skipFrames :: Int } -> ComponentName
Generates a ComponentName
to be passed to mkHook
, like
uniqueNameFromCurrentCallStack
, but logs the stack trace and specific
line to the console.
#withHook Source
withHook :: forall a. Hook a -> (a -> ReactElement) -> ReactElement
When there is only one hook, it might make more sense to invoke it with
continuation-passing style. This helper makes that easier, accepting a
render
callback.
Intended to be used via the ==>
operator:
view :: ReactElement
view = useState "" ==> \(name /\ setName) ->
H.input_ "" { value: name, onChange: setName <?| eventTargetValue }
#withHookCurried Source
withHookCurried :: forall a b. Hook (a /\ b) -> (a -> b -> ReactElement) -> ReactElement
Given a Hook (a /\ b)
, this allows invoking it with a curried render
callback.
Intended to be used via the =/>
operator:
view :: ReactElement
view = useState "" =/> \name setName ->
H.input_ "" { value: name, onChange: setName <?| eventTargetValue }
#withHooks Source
withHooks :: Hook ReactElement -> ReactElement
Unwraps a Hook ReactElement
, which is usually created by using one or
more hooks and then using pure
to encapsulate a ReactElement
. E.g.:
view :: ReactElement
view = withHooks do
name /\ setName <- useState ""
pure $ H.input_ "" { value: name, onChange: setName <?| eventTargetValue }