Elmish.Hooks.Type
- Package
- purescript-elmish-hooks
- Repository
- collegevine/purescript-elmish-hooks
#Hook Source
newtype Hook aThe 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 aGiven 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 } -> ComponentNameGenerates a ComponentName to be passed to mkHook.
#uniqueNameFromCurrentCallStackTraced Source
uniqueNameFromCurrentCallStackTraced :: DebugWarning => { prefix :: String, skipFrames :: Int } -> ComponentNameGenerates 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) -> ReactElementWhen 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) -> ReactElementGiven 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 -> ReactElementUnwraps 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 }