Skip to content

Commit dcde061

Browse files
committed
Move props section to communication.
Props are technically considered `Component` communication. - [x] Rearrange docs
1 parent 89f2e43 commit dcde061

1 file changed

Lines changed: 145 additions & 144 deletions

File tree

src/Miso.hs

Lines changed: 145 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -256,149 +256,6 @@
256256
--
257257
-- 'startApp' and 'miso' will always infer @parent@ as 'ROOT'.
258258
--
259-
-- = Props
260-
--
261-
-- Inspired by [React props](https://react.dev/learn/passing-props-to-a-component),
262-
-- @miso@ allows a parent 'Component' to pass read-only data down to a child 'Component'
263-
-- via a mechanism called /props/ (short for /properties/).
264-
--
265-
-- == Props vs. Component-local state
266-
--
267-
-- * __model__: Component-local state. It is owned and mutated exclusively by the 'Component'
268-
-- itself through its 'Miso.Types.update' function. No other 'Component' can write to it directly.
269-
--
270-
-- * __props__: Data /inherited/ from the @parent@ 'Component'. Props flow downward through
271-
-- the component hierarchy and are read-only from the child's perspective. The parent decides
272-
-- what props to pass at mount time; the child cannot mutate them. Props that change in a
273-
-- the parent cause the child to re-render.
274-
--
275-
-- This mirrors the distinction in React between component state (@useState@) and props
276-
-- received from above (@function MyComponent({ name }) { ... }@).
277-
--
278-
-- === When to use props
279-
--
280-
-- Props are best suited for /metadata/ — contextual or configuration data that the child needs
281-
-- to know about but should not own. Good examples: a user's display name, a theme token, a
282-
-- locale string, or a read-only identifier used to customise rendering.
283-
--
284-
-- If the data drives the child's own business logic — counters it increments, form fields it
285-
-- edits, async state it manages — that data belongs in the child's @model@ instead. Putting
286-
-- mutable business-logic state in @props@ would require the parent to own and thread through
287-
-- every change, creating unnecessary coupling. Prefer @props@ for \"what the child should
288-
-- know\" and @model@ for \"what the child should do\".
289-
--
290-
-- == Props in 'Miso.Types.view'
291-
--
292-
-- The 'Miso.Types.view' field of a 'Component' always takes @props@ as its first argument:
293-
--
294-
-- @
295-
-- view :: props -> model -> 'View' model action
296-
-- @
297-
--
298-
-- Top-level applications have no parent, so @props@ is always @()@:
299-
--
300-
-- @
301-
-- view :: () -> model -> 'View' model action
302-
-- view _props model = …
303-
-- @
304-
--
305-
-- == Props in 'Effect' \/ 'Miso.Types.update'
306-
--
307-
-- Use 'getProps' inside the 'Effect' monad to read the current value of @props@:
308-
--
309-
-- @
310-
-- update :: Action -> 'Effect' parent props Model Action
311-
-- update = \\case
312-
-- SomeAction -> do
313-
-- p <- 'getProps'
314-
-- 'io_' ('consoleLog' (ms (show p)))
315-
-- @
316-
--
317-
-- Alternatively, use the 'Miso.Lens.view' combinator with the 'props' lens:
318-
--
319-
-- @
320-
-- update = \\case
321-
-- SomeAction -> do
322-
-- p <- 'Miso.Lens.view' 'props'
323-
--
324-
-- @
325-
--
326-
-- == 'ROOT' — the top-level 'Component'
327-
--
328-
-- When a 'Component' is passed to 'startApp' (or 'miso') it has no parent.
329-
-- The @parent@ type is specialized to 'ROOT' and @props@ is fixed to @()@:
330-
--
331-
-- @
332-
-- type 'App' model action = 'Component' 'ROOT' () model action
333-
-- @
334-
--
335-
-- Because there is no parent to inherit from, @props@ will always be @()@ for a
336-
-- root-level 'Component'. You can simply ignore the first argument in 'view' and
337-
-- skip 'getProps' in 'Miso.Types.update'.
338-
--
339-
-- == Passing props to a child 'Component'
340-
--
341-
-- Use 'mountWithProps_' (keyed) or 'mountWithProps' (unkeyed) in the parent's 'view' to
342-
-- mount a child and supply its props:
343-
--
344-
-- @
345-
-- 'mountWithProps_'
346-
-- :: ('Eq' child, 'Eq' props)
347-
-- => 'MisoString'
348-
-- -> props
349-
-- -> 'Component' parent props child action
350-
-- -> 'View' parent a
351-
-- @
352-
--
353-
-- == Example: child reading parent-supplied props
354-
--
355-
-- The following shows a parent 'Component' that maintains a greeting string in its
356-
-- @model@ and passes it as @props@ to a child 'Component'. The child renders the
357-
-- greeting and can also read it from within its 'Miso.Types.update' function.
358-
--
359-
-- @
360-
-- -----------------------------------------------------------------------------
361-
-- -- The props type: what the parent shares with the child
362-
-- newtype Greeting = Greeting 'MisoString' deriving ('Eq')
363-
-- -----------------------------------------------------------------------------
364-
-- -- Child component
365-
-- --
366-
-- -- parent props model action
367-
-- -- | | | |
368-
-- child :: 'Component' ParentModel Greeting () ChildAction
369-
-- child = 'vcomp' () updateChild viewChild
370-
-- where
371-
-- viewChild :: Greeting -> () -> 'View' () ChildAction
372-
-- viewChild (Greeting g) _ =
373-
-- 'div_' [] [ 'text' ("Hello, " <> g <> "!") ]
374-
--
375-
-- updateChild :: ChildAction -> 'Effect' ParentModel Greeting () ChildAction
376-
-- updateChild = \\case
377-
-- ReadGreeting -> do
378-
-- Greeting g <- 'getProps'
379-
-- 'io_' ('consoleLog' g)
380-
-- -----------------------------------------------------------------------------
381-
-- -- Parent component: owns the greeting, passes it to the child as props
382-
-- parent :: 'App' ParentModel ParentAction
383-
-- parent = 'vcomp' (ParentModel "World") 'noop' viewParent
384-
-- where
385-
-- viewParent :: () -> ParentModel -> 'View' ParentModel ParentAction
386-
-- viewParent _ (ParentModel g) =
387-
-- 'mountWithProps_' "child" (Greeting g) child
388-
-- -----------------------------------------------------------------------------
389-
-- newtype ParentModel = ParentModel 'MisoString' deriving ('Eq')
390-
-- data ChildAction = ReadGreeting
391-
-- data ParentAction
392-
-- @
393-
--
394-
-- A few things to notice:
395-
--
396-
-- * The child's @parent@ type parameter is @ParentModel@. This must match the
397-
-- parent 'Component' @model@ type — 'mountWithProps_' enforces this at compile time.
398-
-- * 'getProps' inside the child's 'Miso.Types.update' yields a @Greeting@, not
399-
-- the full @ParentModel@. The child only sees what the parent explicitly chose to share.
400-
-- * The root 'App' always has @props ~ ()@; no extra plumbing is needed when calling 'startApp'.
401-
--
402259
-- = 'VComp' lifecycle hooks
403260
--
404261
-- 'Component' are mounted on the fly during diffing. All t'Component` are equipped with `mount` and `unmount` hooks. This allows the defining of custom actions that will be processed in response to lifecycle events.
@@ -459,7 +316,7 @@
459316
--
460317
-- @
461318
-- -- Keyed fragment — survives reordering without full teardown\/remount
462-
-- 'vfrag_' "my-key" [ 'li_' [] [ 'text' "A" ], 'li_' [] [ 'text' "B" ] ]
319+
-- 'vfrag_' "my-key" [ 'li_' [] [ 'text' "Item A" ], 'li_' [] [ 'text' "Item B" ] ]
463320
-- @
464321
--
465322
-- Fragments may be nested — a 'VFrag' child may itself be a 'VFrag'. The diff function
@@ -617,6 +474,150 @@
617474
--
618475
-- = 'Component' communication
619476
--
477+
-- = Props
478+
--
479+
-- Inspired by [React props](https://react.dev/learn/passing-props-to-a-component),
480+
-- @miso@ allows a parent 'Component' to pass read-only data down to a child 'Component'
481+
-- via a mechanism called /props/ (short for /properties/).
482+
--
483+
-- == Props vs. Component-local state
484+
--
485+
-- * __model__: Component-local state. It is owned and mutated exclusively by the 'Component'
486+
-- itself through its 'Miso.Types.update' function. No other 'Component' can write to it directly.
487+
--
488+
-- * __props__: Data /inherited/ from the @parent@ 'Component'. Props flow downward through
489+
-- the component hierarchy and are read-only from the child's perspective. The parent decides
490+
-- what props to pass at mount time; the child cannot mutate them. Props that change in a
491+
-- the parent cause the child to re-render.
492+
--
493+
-- This mirrors the distinction in React between component state (@useState@) and props
494+
-- received from above (@function MyComponent({ name }) { ... }@).
495+
--
496+
-- === When to use props
497+
--
498+
-- Props are best suited for /metadata/ — contextual or configuration data that the child needs
499+
-- to know about but should not own. Good examples: a user's display name, a theme token, a
500+
-- locale string, or a read-only identifier used to customise rendering.
501+
--
502+
-- If the data drives the child's own business logic — counters it increments, form fields it
503+
-- edits, async state it manages — that data belongs in the child's @model@ instead. Putting
504+
-- mutable business-logic state in @props@ would require the parent to own and thread through
505+
-- every change, creating unnecessary coupling. Prefer @props@ for \"what the child should
506+
-- know\" and @model@ for \"what the child should do\".
507+
--
508+
-- == Props in 'Miso.Types.view'
509+
--
510+
-- The 'Miso.Types.view' field of a 'Component' always takes @props@ as its first argument:
511+
--
512+
-- @
513+
-- view :: props -> model -> 'View' model action
514+
-- @
515+
--
516+
-- Top-level applications have no parent, so @props@ is always @()@:
517+
--
518+
-- @
519+
-- view :: () -> model -> 'View' model action
520+
-- view _props model = …
521+
-- @
522+
--
523+
-- == Props in 'Effect' \/ 'Miso.Types.update'
524+
--
525+
-- Use 'getProps' inside the 'Effect' monad to read the current value of @props@:
526+
--
527+
-- @
528+
-- update :: Action -> 'Effect' parent props Model Action
529+
-- update = \\case
530+
-- SomeAction -> do
531+
-- p <- 'getProps'
532+
-- 'io_' ('consoleLog' (ms (show p)))
533+
-- @
534+
--
535+
-- Alternatively, use the 'Miso.Lens.view' combinator with the 'props' lens:
536+
--
537+
-- @
538+
-- update = \\case
539+
-- SomeAction -> do
540+
-- p <- 'Miso.Lens.view' 'props'
541+
--
542+
-- @
543+
--
544+
-- == 'ROOT' — the top-level 'Component'
545+
--
546+
-- When a 'Component' is passed to 'startApp' (or 'miso') it has no parent.
547+
-- The @parent@ type is specialized to 'ROOT' and @props@ is fixed to @()@:
548+
--
549+
-- @
550+
-- type 'App' model action = 'Component' 'ROOT' () model action
551+
-- @
552+
--
553+
-- Because there is no parent to inherit from, @props@ will always be @()@ for a
554+
-- root-level 'Component'. You can simply ignore the first argument in 'view' and
555+
-- skip 'getProps' in 'Miso.Types.update'.
556+
--
557+
-- == Passing props to a child 'Component'
558+
--
559+
-- Use 'mountWithProps_' (keyed) or 'mountWithProps' (unkeyed) in the parent's 'view' to
560+
-- mount a child and supply its props:
561+
--
562+
-- @
563+
-- 'mountWithProps_'
564+
-- :: ('Eq' child, 'Eq' props)
565+
-- => 'MisoString'
566+
-- -> props
567+
-- -> 'Component' parent props child action
568+
-- -> 'View' parent a
569+
-- @
570+
--
571+
-- == Example: child reading parent-supplied props
572+
--
573+
-- The following shows a parent 'Component' that maintains a greeting string in its
574+
-- @model@ and passes it as @props@ to a child 'Component'. The child renders the
575+
-- greeting and can also read it from within its 'Miso.Types.update' function.
576+
--
577+
-- @
578+
-- -----------------------------------------------------------------------------
579+
-- -- The props type: what the parent shares with the child
580+
-- newtype Greeting = Greeting 'MisoString' deriving ('Eq')
581+
-- -----------------------------------------------------------------------------
582+
-- -- Child component
583+
-- --
584+
-- -- parent props model action
585+
-- -- | | | |
586+
-- child :: 'Component' ParentModel Greeting () ChildAction
587+
-- child = 'vcomp' () updateChild viewChild
588+
-- where
589+
-- viewChild :: Greeting -> () -> 'View' () ChildAction
590+
-- viewChild (Greeting g) _ =
591+
-- 'div_' [] [ 'text' ("Hello, " <> g <> "!") ]
592+
--
593+
-- updateChild :: ChildAction -> 'Effect' ParentModel Greeting () ChildAction
594+
-- updateChild = \\case
595+
-- ReadGreeting -> do
596+
-- Greeting g <- 'getProps'
597+
-- 'io_' ('consoleLog' g)
598+
-- -----------------------------------------------------------------------------
599+
-- -- Parent component: owns the greeting, passes it to the child as props
600+
-- parent :: 'App' ParentModel ParentAction
601+
-- parent = 'vcomp' (ParentModel "World") 'noop' viewParent
602+
-- where
603+
-- viewParent :: () -> ParentModel -> 'View' ParentModel ParentAction
604+
-- viewParent _ (ParentModel g) =
605+
-- 'mountWithProps_' "child" (Greeting g) child
606+
-- -----------------------------------------------------------------------------
607+
-- newtype ParentModel = ParentModel 'MisoString' deriving ('Eq')
608+
-- data ChildAction = ReadGreeting
609+
-- data ParentAction
610+
-- @
611+
--
612+
-- A few things to notice:
613+
--
614+
-- * The child's @parent@ type parameter is @ParentModel@. This must match the
615+
-- parent 'Component' @model@ type — 'mountWithProps_' enforces this at compile time.
616+
-- * 'getProps' inside the child's 'Miso.Types.update' yields a @Greeting@, not
617+
-- the full @ParentModel@. The child only sees what the parent explicitly chose to share.
618+
-- * The root 'App' always has @props ~ ()@; no extra plumbing is needed when calling 'startApp'.
619+
--
620+
--
620621
-- == Asynchronous communication
621622
--
622623
-- 'Component' are able to communicate asynchronously via a message-passing system.

0 commit comments

Comments
 (0)