-
Notifications
You must be signed in to change notification settings - Fork 31
/
AffEx.purs
109 lines (100 loc) · 3.37 KB
/
AffEx.purs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
module AffEx where
import Prelude
import Data.Either (either)
import Data.Maybe (Maybe(..), maybe)
import Effect (Effect)
import Effect.Aff (Aff, Milliseconds(..), delay, error, message, throwError)
import React.Basic.DOM as R
import React.Basic.Events (handler_)
import React.Basic.Hooks (type (/\), ReactComponent, Hook, JSX, component, element, fragment, useState, (/\))
import React.Basic.Hooks as React
import React.Basic.Hooks.Aff (useAff)
mkAffEx :: Effect (ReactComponent {})
mkAffEx = do
-- A component for fetching and rendering a Cat entity.
catDetails <- mkCatDetails
component "AffEx" \props -> React.do
catKey /\ catChooser <- useCatKeyChooser
pure
$ R.div
{ style: R.css { display: "flex", flexFlow: "column" }
, children:
[ R.h2_ [ R.text "Cat chooser" ]
, R.p_
[ R.text
$ "Select a key to fetch! If you get bored (how would you even!?) "
<> "try holding your arrow keys to select really fast! The result "
<> "always matches the chosen key."
]
, catChooser
, R.p_
[ case catKey of
Nothing -> mempty
Just k -> element catDetails { catKey: k }
]
]
}
where
-- This hook packages up some interactive UI and the current
-- selection the user has made via that UI.
useCatKeyChooser :: Hook _ ((Maybe (Key Cat)) /\ JSX)
useCatKeyChooser = React.do
catKey /\ setCatKey <- useState Nothing
let
catChoice key =
R.label_
[ R.input
{ type: "radio"
, name: "cat-key"
, checked: Just key == catKey
, onChange:
handler_ do
setCatKey \_ -> Just key
}
, R.text $ showCatKey key
]
showCatKey :: Key Cat -> String
showCatKey (Key key) = "Cat " <> key
pure $ catKey
/\ fragment
[ catChoice $ Key "abc"
, catChoice $ Key "def"
, catChoice $ Key "ghi"
, catChoice $ Key "xyz"
]
-- Hooks can't be used conditionally but components can!
-- Not needing to deal with a `Maybe` key simplifies this
-- compoennt a bit.
mkCatDetails :: Effect (ReactComponent { catKey :: Key Cat })
mkCatDetails = do
component "CatDetails" \{ catKey } -> React.do
cat <- useAff catKey $ fetch catKey
pure $ R.text
$ maybe "Loading..." (either message showCat) cat
where
showCat (Cat { name }) = "A cat named " <> name
-- Typed keys are a great way to tie entity-specific behavior
-- to an ID. We can use this phantom type to write a class
-- for generic, type-safe data fetching.
newtype Key entity
= Key String
derive instance eqKey :: Eq (Key entity)
class Fetch entity where
fetch :: Key entity -> Aff entity
-- An example entity
newtype Cat
= Cat { name :: String }
instance fetchCat :: Fetch Cat where
fetch = case _ of
Key "abc" -> do
delay $ Milliseconds 300.0
pure $ Cat { name: "Herb" }
Key "def" -> do
delay $ Milliseconds 600.0
pure $ Cat { name: "Maxi" }
Key "ghi" -> do
delay $ Milliseconds 900.0
pure $ Cat { name: "Chloe" }
_ -> do
delay $ Milliseconds 900.0
throwError $ error "Cat not found (intended example behavior 😅)"