Elmish.Hooks.Type
- Package
- purescript-elmish-hooks
- Repository
- collegevine/purescript-elmish-hooks
#ComposedHookTypes Source
class ComposedHookTypes :: HookType' -> HookType' -> HookType' -> Constraint
class ComposedHookTypes (left :: HookType') (right :: HookType') (result :: HookType') | left right -> result
This class represents the type level function for composing HookType'
s,
with instances for appending the identity and arbitrary HookType'
s.
Instances
ComposedHookTypes Pure t t
ComposedHookTypes t Pure t
(ComposedHookTypes l2 right right') => ComposedHookTypes (AppendHookType l1 l2) right (AppendHookType l1 right')
#AppendHookType Source
type AppendHookType :: (HookType' -> HookType') -> HookType' -> HookType'
type AppendHookType (h :: HookType) t = h t
A type which allows appending HookType
s via type application.
Instances
(ComposedHookTypes l2 right right') => ComposedHookTypes (AppendHookType l1 l2) right (AppendHookType l1 right')
#Hook' Source
data Hook' :: HookType' -> Type -> Type
data Hook' (t :: HookType') 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:
Hooks.component do
foo /\ setFoo <- useState ""
pure …
Finally, to make sure we don’t use two hooks with different state-types in
a conditional (which could cause unexpected issues for React) we have a
HookType'
parameter, which accumulates when we call bind
. For this we
need to use qualified do notation:
Hooks.component Hooks.do
foo /\ setFoo <- useState ""
Hooks.pure …
Instances
#HookType' Source
data HookType'
Represents the type of a hook and is used to ensure hooks are safe. For
example, the following will not compile because we track HookType
:
Hooks.component Hooks.do
if someCondition then Hooks.do
x <- useState ""
_ <- useState 0
Hooks.pure x
else Hooks.do
_ <- useState 0
useState ""
because the first block has a HookType
of UseState String <> UseState
Int
and the second is UseState Int <> UseState String
. The same hooks
need to be used in the same order for the hook types to match.
#Pure Source
#type (<>) Source
Operator alias for Elmish.Hooks.Type.AppendHookType (right-associative / precedence 6)
#bind Source
bind :: forall ta tb tr a b. ComposedHookTypes ta tb tr => Hook' ta a -> (a -> Hook' tb b) -> Hook' tr b
#component Source
component :: forall t. Hook' t 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 = Hooks.component Hooks.do
name /\ setName <- useState ""
Hooks.pure $ H.input_ "" { value: name, onChange: setName <?| eventTargetValue }
#mkHook Source
mkHook :: forall msg state t a. ComponentName -> ((a -> ReactElement) -> ComponentDef msg state) -> Hook' t a
Given a ComponentName
and a function to create a ComponentDef
(from a
render function a -> ReactElement
), mkHook
creates a Hook a
. When
creating a hook with mkHook
, you’ll need to create a HookType
by
foreign import
ing it.
As an example of how to use mkHook
, useEffect
uses it like so:
foreign import data UseEffect :: Type -> HookType
useEffect :: Aff Unit -> Hook (UseEffect Unit) Unit
useEffect init =
mkHook (ComponentName "UseEffect") \render ->
{ init: forkVoid init
, update: \_ msg -> absurd msg
, view: \_ _ -> render unit
}
#withHook Source
withHook :: forall t a. Hook' t 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 t a b. Hook' t (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 }