Skip to content

Commit 1abbdaf

Browse files
authored
Update (#2)
1 parent f624175 commit 1abbdaf

File tree

7 files changed

+274
-143
lines changed

7 files changed

+274
-143
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
.psc-ide-port
55
.pulp-cache/
66
npm-debug.log
7+
yarn-error.log
78
node_modules/
89
bower_components/
910
tmp/

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
"dependencies": {
99
"purescript-console": "^3.0.0",
1010
"purescript-js-timers": "^3.0.0",
11-
"purescript-react-redux": "^5.1.0"
11+
"purescript-react-redux": "^6.0.0"
1212
}
1313
}

package.json

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,59 +2,24 @@
22
"name": "purescript-react-redux-example",
33
"version": "0.1.0",
44
"private": true,
5-
"flat": false,
65
"files": [],
76
"scripts": {
87
"webpack": "webpack --progress",
98
"webpack:watch": "webpack --progress --watch",
10-
"webpack:server": "DEBUG=purs-loader webpack-dev-server --progress --inline --hot",
9+
"webpack-dev-server": "DEBUG=purs-loader webpack-dev-server --progress --inline --hot",
1110
"purs:compile": "purs compile 'bower_components/purescript-*/src/**/*.purs' 'src/**/*.purs'",
1211
"postinstall": "bower i"
1312
},
1413
"dependencies": {
14+
"pscid": "^2.0.2",
1515
"purescript": "^0.11.6",
1616
"purescript-psa": "^0.5.1",
1717
"purs-loader": "^3.1.1",
1818
"react": "^15.3.2",
1919
"react-dom": "^15.3.2",
20-
"react-redux": "^4.4.5",
20+
"react-redux": "^5.0.6",
2121
"redux": "^3.6.0",
2222
"webpack": "^1.13.3",
2323
"webpack-dev-server": "^1.16.2"
24-
},
25-
"resolutions": {
26-
"readable-stream": "2.2.1",
27-
"vinyl": "1.2.0",
28-
"inherits": "2.0.3",
29-
"object-assign": "4.1.0",
30-
"through2": "2.0.1",
31-
"minimist": "1.2.0",
32-
"supports-color": "3.1.2",
33-
"end-of-stream": "1.1.0",
34-
"once": "1.4.0",
35-
"commander": "2.9.0",
36-
"duplexer2": "0.1.4",
37-
"glob": "7.1.1",
38-
"glob-parent": "3.0.1",
39-
"is-glob": "3.1.0",
40-
"camelcase": "2.1.1",
41-
"semver": "5.3.0",
42-
"debug": "2.3.2",
43-
"async": "1.5.2",
44-
"memory-fs": "0.3.0",
45-
"punycode": "1.4.1",
46-
"wordwrap": "0.0.3",
47-
"source-map": "0.5.6",
48-
"qs": "6.3.0",
49-
"assert-plus": "1.0.0",
50-
"mime-db": "1.25.0",
51-
"faye-websocket": "0.11.0",
52-
"url-parse": "1.1.7",
53-
"isarray": "1.0.0",
54-
"is-extglob": "2.1.0",
55-
"ms": "0.7.2"
56-
},
57-
"devDependencies": {
58-
"process-nextick-args": "^1.0.7"
5924
}
6025
}

src/Example/App.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
'use strict';
22

3-
exports.reduxDevtoolsExtensionEnhancer = !window.__REDUX_DEVTOOLS_EXTENSION__ ? function reduxDevtoolsExtensionEnhancer (a) {
4-
return a;
5-
} : window.__REDUX_DEVTOOLS_EXTENSION__();
3+
exports.reduxDevtoolsExtensionEnhancer = !window.__REDUX_DEVTOOLS_EXTENSION__ ? function reduxDevtoolsExtensionEnhancer (a) { return a; } : window.__REDUX_DEVTOOLS_EXTENSION__();

src/Example/App.purs

Lines changed: 128 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,35 @@
11
module Example.App (main) where
22

3-
import Prelude (Unit, (<$>), (+), (<>), (<<<), bind, id, pure, show, void)
3+
import Prelude
44

55
import Control.Monad.Eff (Eff)
66
import Control.Monad.Eff.Console (CONSOLE, info)
77
import Control.Monad.Eff.Timer (TIMER, setTimeout)
88
import Control.Monad.Eff.Unsafe (unsafeCoerceEff)
99

10-
import Data.Lens (Lens', Prism', lens, prism')
11-
import Data.Maybe (Maybe(..))
10+
import Data.Maybe (Maybe(..), maybe)
11+
import Data.Newtype (wrap)
1212

1313
import React as React
1414
import React.DOM as DOM
1515
import React.DOM.Props as Props
1616

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
1830

1931
type State = { counterA :: Int, counterB :: Int }
2032

21-
type StateA = { counterA :: Int }
22-
23-
type StateB = { counterB :: Int }
24-
2533
data Action = ActionA ActionA | ActionB ActionB
2634

2735
data ActionA = IncrementA | DelayedIncrementA Int
@@ -30,97 +38,136 @@ data ActionB = IncrementB
3038

3139
type Effect eff = (console :: CONSOLE, timer :: TIMER | eff)
3240

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)
3543
where
3644
initialState :: State
3745
initialState = { counterA: 0, counterB: 0 }
3846

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"
4165

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)
4470

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
5175
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
5481

55-
showAction :: String
56-
showAction =
82+
reducerB action state =
5783
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
6186

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+
}
6791

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
10294
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
105157
where
106-
render' :: State -> React.ReactElement
107-
render' props =
158+
render' :: AppProps -> React.ReactElement
159+
render' _ =
108160
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 []
115163
]
116-
where
117-
onClick :: Action -> React.Event -> React.EventHandlerContext eff Unit Unit Unit
118-
onClick action event = void (unsafeCoerceEff (dispatch (pure action)))
119164

120165
main :: forall eff. Eff (Effect (Redux.ReduxEffect eff)) React.ReactElement
121166
main = do
122167
store' <- store
123-
let element = Redux.createProviderElement store' appClass
168+
169+
let element = Redux.createProviderElement store' [ React.createElement appClass unit [] ]
170+
124171
pure element
125172

126-
foreign import reduxDevtoolsExtensionEnhancer :: forall action state. Redux.EnhancerForeign action state
173+
foreign import reduxDevtoolsExtensionEnhancer :: forall eff state action. Redux.ReduxStoreEnhancer eff state action

webpack.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module.exports = {
55

66
debug: true,
77

8-
devtool: 'eval',
8+
devtool: 'source-map',
99

1010
devServer: {
1111
contentBase: '.',
@@ -27,6 +27,9 @@ module.exports = {
2727
exclude: /node_modules/,
2828
query: {
2929
psc: 'psa',
30+
pscArgs: {
31+
sourceMaps: false
32+
},
3033
src: [
3134
'bower_components/purescript-*/src/**/*.purs',
3235
'src/Example/**/*.purs'

0 commit comments

Comments
 (0)