Elmish.Component
- Package
- purescript-elmish
- Repository
- collegevine/purescript-elmish
#Transition Source
data Transition m msg state
A UI component state transition: wraps the new state value together with a (possibly empty) list of effects that the transition has caused, with each effect ultimately producing a new message.
Constructors
Transition state (Array (m msg))
Instances
(Functor m) => Bifunctor (Transition m)
Functor (Transition m msg)
Apply (Transition m msg)
Applicative (Transition m msg)
#ComponentDef Source
type ComponentDef m msg state = { init :: Transition m msg state, update :: state -> msg -> Transition m msg state, view :: state -> DispatchMsgFn msg -> ReactElement }
Definition of a component according to The Elm Architecture. Consists of three functions - init, view, update, - that together describe the lifecycle of a component.
Type parameters:
m
- a monad in which the effects produced byupdate
andinit
functions run.msg
- component's message.state
- component's state.
#ComponentReturnCallback Source
type ComponentReturnCallback m a = forall msg state. ComponentDef m msg state -> a
A callback used to return multiple components of different types. See below for more a detailed explanation.
This callback is handy in situations where a function must return different
components (with different state
and message
types) depending on
parameters. The prime example of such situation is routing.
Because most routes are served by different UI components, with different
state
and message
type parameters, the instantiating functions cannot
have the naive signature route -> component
: they need to "return"
differently-typed results depending on the route. In order to make that
happen, these functions instead take a polymorphic callback, to which they
pass the UI component. This type alias is the type of such callback: it
takes a polymorphically-typed UI component and returns "some value", a la
continuation-passing style.
Even though this type is rather trivial, it is included in the library for the purpose of attaching this documentation to it.
#(<$$>) Source
Operator alias for Elmish.Component.mapCmds (non-associative / precedence 8)
A nested map
- useful for mapping over commands in an array: first map
maps over the array, second map
maps over the monad m
.
Example:
let (Transition subS cmds) = SubComponent.update s.subComponent msg
in Transition (s { subComponent = subS }) (SubComponentMsg <$$> cmds)
#pureUpdate Source
pureUpdate :: forall state msg m. state -> Transition m msg state
Creates a Transition
without any commands.
This function will be deprecated soon in favor of pure
.
#withTrace Source
withTrace :: forall state msg m. DebugWarning => ComponentDef m msg state -> ComponentDef m msg state
Wraps the given component, intercepts its update cycle, and traces (i.e. prints to dev console) every command and every state value (as JSON objects).
#nat Source
nat :: forall state msg n m. (forall a. m a -> n a) -> ComponentDef m msg state -> ComponentDef n msg state
Monad transformation applied to ComponentDef
#construct Source
construct :: forall state msg. ComponentDef Aff msg state -> Effect (DispatchMsgFn Unit -> ReactElement)
Given a ComponentDef, binds that def to a freshly created React class,
instantiates that class, and returns a rendering function. Note that the
return type of this function is almost the same as that of
ComponentDef::view
- except for state. This is not a coincidence: it is
done this way on purpose, so that the result of this call can be used to
construct another ComponentDef
.
Unlike wrapWithLocalState
, this function uses the bullet-proof strategy
of storing the component state in a dedicated mutable cell, but that
happens at the expense of being effectful.
#wrapWithLocalState Source
wrapWithLocalState :: forall args state msg. ComponentName -> (args -> ComponentDef Aff msg state) -> DispatchMsgFn Unit -> args -> ReactElement
Creates a React component that can be bound to a varying ComponentDef, returns a function that performs the binding.
Note 1: this function accepts an Aff
-based ComponentDef, it cannot take
polymorphic or custom monad. The superficial reason for this is that this
function is intended to be used at top-level (see explanation below), where
context for a custom monad is not available. A deeper reason is that this
function creates a self-contained React component, and it is precisely
because it is self-contained that it cannot be seamlessly included in an
outer monadic computation.
This limitation forces such truly "reusable" components to be written in
terms of Aff
rather than a custom monad, which is actually a good thing.
However, if it turns out that this component really needs to be in a custom
monad, it is always possible to convert it to Aff
via the nat
function.
Note 2: in order to accomplish this, such aggregated component will store
its state using the React facilities - i.e. via this.setState
and
this.state
. While this is appropriate for most cases, it actually has
proven to be fragile in some specific circumstances (e.g. multiple events
occurring within the same JS synchronous frame), so it is not recommended
to use this mechanism for complex components.
Note 3: this function has to be called exactly once, at top-level. It
cannot be called in the parent's view
function on every render. Every
time it is called it creates a new React class, so if it was called on
every render, the resulting JSX DOM will contain a new component every
time, which will make React remount that component and destroy its local
state. See explanation on withCachedComponent
for more.
#ComponentName Source
newtype ComponentName
A unique name for a component created via wrapWithLocalState
. These names
don't technically need to be completely unique, but they do need to be
unique enough so that two different wrapWithLocalState
-created components
that happen to have the same name never replace each other in the DOM. For
this reason, it is recommended to actually make sure these names are
unique, for example by appending a GUID to them. Read on for a more
detailed explanation.
React uses referential equality to decide whether to create a new instance of a component (and thus reset its local state) or keep the existing instance. This means that, on one hand, we cannot use the same React class for every instantiation, because this may create conflicts, where one Elmish component replaces another in the DOM, but they look like the same component to React, which makes it reuse state, which leads to chaos. On the other hand, we cannot create a fresh class on every render, because then React will see it as a new component every time, and will reset its state every time.
This means that we need some way of figuring out whether it needs to be
logically "same" component or "different", but there is no way to get that
"for free" (same way React gets it for free from referential equality) due
to PureScript's purity. Therefore, the only reliable way is to ask the
programmer, which is accomplished by requiring a ComponentName
, which
serves as a key.