1
1
module Example.App (main ) where
2
2
3
- import Prelude ( Unit , (<$>), (+), (<>), (<<<), bind , id , pure , show , void )
3
+ import Prelude
4
4
5
5
import Control.Monad.Eff (Eff )
6
6
import Control.Monad.Eff.Console (CONSOLE , info )
7
7
import Control.Monad.Eff.Timer (TIMER , setTimeout )
8
8
import Control.Monad.Eff.Unsafe (unsafeCoerceEff )
9
9
10
- import Data.Lens ( Lens' , Prism' , lens , prism' )
11
- import Data.Maybe ( Maybe (..) )
10
+ import Data.Maybe ( Maybe (..), maybe )
11
+ import Data.Newtype ( wrap )
12
12
13
13
import React as React
14
14
import React.DOM as DOM
15
15
import React.DOM.Props as Props
16
16
17
- import React.Redux as Redux
17
+ import React.Redux
18
+ ( BaseDispatch
19
+ , ConnectClass'
20
+ , Reducer
21
+ , ReduxEffect
22
+ , ReduxStore
23
+ , ReduxStoreEnhancer
24
+ , applyMiddleware
25
+ , connect_
26
+ , createElement_
27
+ , createProviderElement
28
+ , createStore
29
+ ) as Redux
18
30
19
31
type State = { counterA :: Int , counterB :: Int }
20
32
21
- type StateA = { counterA :: Int }
22
-
23
- type StateB = { counterB :: Int }
24
-
25
33
data Action = ActionA ActionA | ActionB ActionB
26
34
27
35
data ActionA = IncrementA | DelayedIncrementA Int
@@ -30,97 +38,136 @@ data ActionB = IncrementB
30
38
31
39
type Effect eff = (console :: CONSOLE , timer :: TIMER | eff )
32
40
33
- store :: forall eff . Eff (Effect (Redux.ReduxEffect eff )) (Redux.Store Action State )
34
- store = Redux .createStore reducer initialState (middlewareEnhancer <<< reduxDevtoolsExtensionEnhancer' )
41
+ store :: forall eff . Eff (Effect (Redux.ReduxEffect eff )) (Redux.ReduxStore ( Effect eff ) State Action )
42
+ store = Redux .createStore reducer initialState (middlewareEnhancer <<< reduxDevtoolsExtensionEnhancer)
35
43
where
36
44
initialState :: State
37
45
initialState = { counterA: 0 , counterB: 0 }
38
46
39
- reduxDevtoolsExtensionEnhancer' :: Redux.Enhancer (Effect eff ) Action State
40
- reduxDevtoolsExtensionEnhancer' = Redux .fromEnhancerForeign reduxDevtoolsExtensionEnhancer
47
+ middlewareEnhancer :: Redux.ReduxStoreEnhancer (Effect eff ) State Action
48
+ middlewareEnhancer = Redux .applyMiddleware (wrap <$> [ loggerMiddleware, timeoutSchedulerMiddleware ])
49
+ where
50
+ loggerMiddleware { getState, dispatch } next action = do
51
+ _ <- info (showAction action)
52
+ _ <- next action
53
+ state <- getState
54
+ logState state
55
+ where
56
+ logState :: State -> Eff (Effect (Redux.ReduxEffect eff )) Unit
57
+ logState { counterA, counterB } = info (" state = { counterA: " <> show counterA <> " , " <> " counterB: " <> show counterB <> " }" )
58
+
59
+ showAction :: Action -> String
60
+ showAction =
61
+ case _ of
62
+ ActionA IncrementA -> " ActionA IncremementA"
63
+ ActionA (DelayedIncrementA delay) -> " ActionA (DelayedIncrementA " <> show delay <> " )"
64
+ ActionB IncrementB -> " ActionB IncremementB"
41
65
42
- middlewareEnhancer :: Redux.Enhancer (Effect eff ) Action State
43
- middlewareEnhancer = Redux .applyMiddleware [ loggerMiddleware, timeoutSchedulerMiddleware ]
66
+ timeoutSchedulerMiddleware { getState, dispatch } next action =
67
+ case action of
68
+ ActionA (DelayedIncrementA delay) -> void (setTimeout delay (void (next action)))
69
+ _ -> void (next action)
44
70
45
- loggerMiddleware :: Redux.Middleware (Effect eff ) Action State Unit
46
- loggerMiddleware { getState, dispatch } next action = do
47
- _ <- info showAction
48
- _ <- next action
49
- state <- getState
50
- logState state
71
+ reducer :: Redux.Reducer Action State
72
+ reducer =
73
+ wrap reducerA <<<
74
+ wrap reducerB
51
75
where
52
- logState :: State -> Eff (Effect (Redux.ReduxEffect eff )) Unit
53
- logState { counterA, counterB } = info (" state = { counterA: " <> show counterA <> " , " <> " counterB: " <> show counterB <> " }" )
76
+ reducerA action state =
77
+ case action of
78
+ ActionA IncrementA -> state { counterA = state.counterA + 1 }
79
+ ActionA (DelayedIncrementA _) -> state { counterA = state.counterA + 1 }
80
+ _ -> state
54
81
55
- showAction :: String
56
- showAction =
82
+ reducerB action state =
57
83
case action of
58
- ActionA IncrementA -> " ActionA IncremementA"
59
- ActionA (DelayedIncrementA delay) -> " ActionA (DelayedIncrementA " <> show delay <> " )"
60
- ActionB IncrementB -> " ActionB IncremementB"
84
+ ActionB IncrementB -> state { counterB = state.counterB + 1 }
85
+ _ -> state
61
86
62
- timeoutSchedulerMiddleware :: Redux.Middleware (Effect eff ) Action State Unit
63
- timeoutSchedulerMiddleware { getState, dispatch } next action =
64
- case action of
65
- ActionA (DelayedIncrementA delay) -> void (setTimeout delay (void (next action)))
66
- _ -> void (next action)
87
+ type IncrementAProps eff
88
+ = { a :: Int
89
+ , onIncrement :: Maybe Int -> Eff eff Unit
90
+ }
67
91
68
- reducer :: Redux.Reducer Action State
69
- reducer action =
70
- (Redux .reducerOptic lensA prismA reducerA) action <<<
71
- (Redux .reducerOptic lensB prismB reducerB) action
72
- where
73
- reducerA :: Redux.Reducer ActionA StateA
74
- reducerA action' state' =
75
- case action' of
76
- IncrementA -> state' { counterA = state'.counterA + 1 }
77
- DelayedIncrementA _ -> state' { counterA = state'.counterA + 1 }
78
-
79
- lensA :: Lens' State StateA
80
- lensA = lens (\s -> { counterA: s.counterA }) (\s b -> s { counterA = b.counterA })
81
-
82
- prismA :: Prism' Action ActionA
83
- prismA = prism' ActionA (\a -> case a of
84
- ActionA a' -> Just a'
85
- _ -> Nothing )
86
-
87
- reducerB :: Redux.Reducer ActionB StateB
88
- reducerB action' state' =
89
- case action' of
90
- IncrementB -> state' { counterB = state'.counterB + 1 }
91
-
92
- lensB :: Lens' State StateB
93
- lensB = lens (\s -> { counterB: s.counterB }) (\s b -> s { counterB = b.counterB })
94
-
95
- prismB :: Prism' Action ActionB
96
- prismB = prism' ActionB (\a -> case a of
97
- ActionB a' -> Just a'
98
- _ -> Nothing )
99
-
100
- appClass :: Redux.ReduxReactClass' State State
101
- appClass = Redux .createClass' id (Redux .spec' render)
92
+ incrementAClass :: forall eff . React.ReactClass (IncrementAProps eff )
93
+ incrementAClass = React .createClassStateless render
102
94
where
103
- render :: forall eff . Redux.Render State Unit eff (Eff (Redux.ReduxEffect eff )) Action
104
- render dispatch this = render' <$> React .getProps this
95
+ render :: IncrementAProps eff -> React.ReactElement
96
+ render { a
97
+ , onIncrement
98
+ } =
99
+ DOM .div []
100
+ [ DOM .button
101
+ [ Props .onClick (\event -> unsafeCoerceEff (onIncrement Nothing )) ]
102
+ [ DOM .text (" Increment A: " <> show a) ]
103
+ , DOM .button
104
+ [ Props .onClick (const $ unsafeCoerceEff (onIncrement (Just 2000 ))) ]
105
+ [ DOM .text (" Increment A (delayed by 2s): " <> show a) ]
106
+ ]
107
+
108
+ type IncrementBProps eff
109
+ = { b :: Int
110
+ , onIncrement :: Eff eff Unit
111
+ }
112
+
113
+ incrementBClass :: forall eff . React.ReactClass (IncrementBProps eff )
114
+ incrementBClass = React .createClassStateless render
115
+ where
116
+ render :: IncrementBProps eff -> React.ReactElement
117
+ render { b
118
+ , onIncrement
119
+ } =
120
+ DOM .div []
121
+ [ DOM .button
122
+ [ Props .onClick (const $ unsafeCoerceEff onIncrement) ]
123
+ [ DOM .text (" Increment B: " <> show b) ]
124
+ ]
125
+
126
+ incrementAComponent :: forall eff . Redux.ConnectClass' State (IncrementAProps eff ) Action
127
+ incrementAComponent = Redux .connect_ stateToProps dispatchToProps { } incrementAClass
128
+ where
129
+ stateToProps :: State -> { a :: Int }
130
+ stateToProps { counterA } =
131
+ { a: counterA
132
+ }
133
+
134
+ dispatchToProps :: Redux.BaseDispatch eff Action -> { onIncrement :: Maybe Int -> Eff eff Unit }
135
+ dispatchToProps dispatch =
136
+ { onIncrement: void <<< unsafeCoerceEff <<< dispatch <<< ActionA <<< maybe IncrementA DelayedIncrementA
137
+ }
138
+
139
+ incrementBComponent :: forall eff . Redux.ConnectClass' State (IncrementBProps eff ) Action
140
+ incrementBComponent = Redux .connect_ stateToProps dispatchToProps { withRef: true } incrementBClass
141
+ where
142
+ stateToProps { counterB } =
143
+ { b: counterB
144
+ }
145
+
146
+ dispatchToProps dispatch =
147
+ { onIncrement: void (unsafeCoerceEff (dispatch (ActionB IncrementB )))
148
+ }
149
+
150
+ type AppProps = Unit
151
+
152
+ appClass :: React.ReactClass AppProps
153
+ appClass = React .createClass (React .spec unit render)
154
+ where
155
+ render :: forall eff . React.Render AppProps Unit eff
156
+ render this = render' <$> React .getProps this
105
157
where
106
- render' :: State -> React.ReactElement
107
- render' props =
158
+ render' :: AppProps -> React.ReactElement
159
+ render' _ =
108
160
DOM .div []
109
- [ DOM .button [ Props .onClick (onClick (ActionA IncrementA )) ]
110
- [ DOM .text (" Increment A: " <> show props.counterA) ]
111
- , DOM .button [ Props .onClick (onClick (ActionA (DelayedIncrementA 2000 ))) ]
112
- [ DOM .text (" Increment A (delayed by 2s): " <> show props.counterA) ]
113
- , DOM .button [ Props .onClick (onClick (ActionB IncrementB )) ]
114
- [ DOM .text (" Increment B: " <> show props.counterB) ]
161
+ [ Redux .createElement_ incrementAComponent []
162
+ , Redux .createElement_ incrementBComponent []
115
163
]
116
- where
117
- onClick :: Action -> React.Event -> React.EventHandlerContext eff Unit Unit Unit
118
- onClick action event = void (unsafeCoerceEff (dispatch (pure action)))
119
164
120
165
main :: forall eff . Eff (Effect (Redux.ReduxEffect eff )) React.ReactElement
121
166
main = do
122
167
store' <- store
123
- let element = Redux .createProviderElement store' appClass
168
+
169
+ let element = Redux .createProviderElement store' [ React .createElement appClass unit [] ]
170
+
124
171
pure element
125
172
126
- foreign import reduxDevtoolsExtensionEnhancer :: forall action state . Redux.EnhancerForeign action state
173
+ foreign import reduxDevtoolsExtensionEnhancer :: forall eff state action . Redux.ReduxStoreEnhancer eff state action
0 commit comments