Skip to content

Commit 542decc

Browse files
authored
Remove update/action code and re-expose React's setState functions (#73)
We've been finding the update/action pattern to be too verbose for basic use cases. ~There was also a bug with type checking actions correctly (#71).~ This update keeps the current types and lifecycle code while returning state updates to the more traditional `setState` and `setStateThen`.
1 parent e567488 commit 542decc

File tree

23 files changed

+389
-438
lines changed

23 files changed

+389
-438
lines changed

Diff for: examples/actions/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
output
2+
html/index.js
3+
package-lock.json
4+
node_modules

Diff for: examples/actions/Makefile

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
all: node_modules
2+
purs compile src/*.purs '../../src/**/*.purs' '../../bower_components/purescript-*/src/**/*.purs'
3+
purs bundle -m Main --main Main output/*/*.js > output/bundle.js
4+
node_modules/.bin/browserify output/bundle.js -o html/index.js
5+
6+
node_modules:
7+
npm install
8+

Diff for: examples/actions/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Counter Example
2+
3+
## Building
4+
5+
```
6+
npm install
7+
make all
8+
```
9+
10+
This will compile the PureScript source files, bundle them, and use Browserify to combine PureScript and NPM sources into a single bundle.
11+
12+
Then open `html/index.html` in your browser.

Diff for: examples/actions/html/index.html

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>react-basic example</title>
5+
</head>
6+
<body>
7+
<div id="container"></div>
8+
<script src="index.js"></script>
9+
</body>
10+
</html>

Diff for: examples/actions/package.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"dependencies": {
3+
"react": "16.6.0",
4+
"react-dom": "16.6.0"
5+
},
6+
"devDependencies": {
7+
"browserify": "16.2.3"
8+
}
9+
}

Diff for: examples/actions/src/Actions.purs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
module Actions where
2+
3+
import Prelude
4+
5+
import Effect.Console (log)
6+
import React.Basic (Component, JSX, StateUpdate(..), createComponent, make, runUpdate)
7+
import React.Basic.DOM as R
8+
import React.Basic.DOM.Events (capture_)
9+
10+
component :: Component Props
11+
component = createComponent "Counter"
12+
13+
type Props =
14+
{ label :: String
15+
}
16+
17+
data Action
18+
= Increment
19+
20+
actions :: Props -> JSX
21+
actions = make component { initialState, render }
22+
where
23+
initialState = { counter: 0 }
24+
25+
update self = case _ of
26+
Increment ->
27+
UpdateAndSideEffects
28+
(self.state { counter = self.state.counter + 1 })
29+
\{ state } -> log $ "Count: " <> show state.counter
30+
31+
send = runUpdate update
32+
33+
render self =
34+
R.button
35+
{ onClick: capture_ $ send self Increment
36+
, children: [ R.text (self.props.label <> ": " <> show self.state.counter) ]
37+
}

Diff for: examples/actions/src/Main.purs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module Main where
2+
3+
import Prelude
4+
5+
import Actions (actions)
6+
import Data.Maybe (Maybe(..))
7+
import Effect (Effect)
8+
import Effect.Exception (throw)
9+
import React.Basic.DOM (render)
10+
import Web.DOM.NonElementParentNode (getElementById)
11+
import Web.HTML (window)
12+
import Web.HTML.HTMLDocument (toNonElementParentNode)
13+
import Web.HTML.Window (document)
14+
15+
main :: Effect Unit
16+
main = do
17+
container <- getElementById "container" =<< (map toNonElementParentNode $ document =<< window)
18+
case container of
19+
Nothing -> throw "Container element not found."
20+
Just c ->
21+
let app = actions { label: "Increment" }
22+
in render app c

Diff for: examples/async/src/AsyncCounter.purs

+8-14
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import Prelude
55
import Effect.Aff (Milliseconds(..), delay)
66
import Effect.Class (liftEffect)
77
import Effect.Console (log)
8-
import React.Basic (Component, JSX, StateUpdate(..), capture_, createComponent, fragment, keyed, make)
8+
import React.Basic (Component, JSX, createComponent, fragment, keyed, make)
99
import React.Basic.Components.Async (asyncWithLoader)
1010
import React.Basic.DOM as R
11+
import React.Basic.DOM.Events (capture_)
1112

1213
component :: Component Props
1314
component = createComponent "AsyncCounter"
@@ -16,17 +17,10 @@ type Props =
1617
{ label :: String
1718
}
1819

19-
data Action
20-
= Increment
21-
2220
asyncCounter :: Props -> JSX
23-
asyncCounter = make component { initialState, update, render }
21+
asyncCounter = make component { initialState, render }
2422
where
25-
initialState = { counter: 0 }
26-
27-
update self = case _ of
28-
Increment ->
29-
Update self.state { counter = self.state.counter + 1 }
23+
initialState = 0
3024

3125
render self =
3226
fragment
@@ -36,16 +30,16 @@ asyncCounter = make component { initialState, update, render }
3630
, R.li_ [ R.text "\"done\" should only be logged to the console once for any loading period (in-flight requests get cancelled as the next request starts)" ]
3731
]
3832
, R.button
39-
{ onClick: capture_ self Increment
40-
, children: [ R.text (self.props.label <> ": " <> show self.state.counter) ]
33+
{ onClick: capture_ $ self.setState (_ + 1)
34+
, children: [ R.text (self.props.label <> ": " <> show self.state) ]
4135
}
4236
, R.text " "
43-
, keyed (show self.state.counter) $
37+
, keyed (show self.state) $
4438
asyncWithLoader (R.text "Loading...") do
4539
liftEffect $ log "start"
4640
delay $ Milliseconds 2000.0
4741
liftEffect $ log "done"
48-
pure $ R.text $ "Done: " <> show self.state.counter
42+
pure $ R.text $ "Done: " <> show self.state
4943
]
5044

5145

Diff for: examples/component/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
"react-dom": "^16.4.2"
55
},
66
"devDependencies": {
7-
"browserify": "^16.2.2"
7+
"browserify": "16.2.3"
88
}
99
}

Diff for: examples/component/src/ToggleButton.purs

+6-12
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ module ToggleButton where
33
import Prelude
44

55
import Effect.Console (log)
6-
import React.Basic (Component, JSX, StateUpdate(..), capture_, createComponent, make)
6+
import React.Basic (Component, JSX, createComponent, make, readState)
77
import React.Basic.DOM as R
8+
import React.Basic.DOM.Events (capture_)
89

910
component :: Component Props
1011
component = createComponent "ToggleButton"
@@ -13,25 +14,18 @@ type Props =
1314
{ label :: String
1415
}
1516

16-
data Action
17-
= Toggle
18-
1917
toggleButton :: Props -> JSX
2018
toggleButton = make component
2119
{ initialState:
2220
{ on: false
2321
}
2422

25-
, update: \self -> case _ of
26-
Toggle ->
27-
UpdateAndSideEffects
28-
self.state { on = not self.state.on }
29-
\nextSelf -> do
30-
log $ "next state: " <> show nextSelf.state
31-
3223
, render: \self ->
3324
R.button
34-
{ onClick: capture_ self Toggle
25+
{ onClick: capture_ $
26+
self.setStateThen _ { on = not self.state.on } do
27+
nextState <- readState self
28+
log $ "next state: " <> show nextState
3529
, children:
3630
[ R.text self.props.label
3731
, R.text if self.state.on

Diff for: examples/controlled-input/src/ControlledInput.purs

+8-14
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,34 @@ module ControlledInput where
33
import Prelude
44

55
import Data.Maybe (Maybe(..), fromMaybe, maybe)
6-
import React.Basic (Component, JSX, StateUpdate(..), capture, createComponent, make)
6+
import React.Basic (Component, JSX, createComponent, make)
77
import React.Basic as React
88
import React.Basic.DOM as R
9-
import React.Basic.DOM.Events (targetValue, timeStamp)
9+
import React.Basic.DOM.Events (capture, targetValue, timeStamp)
1010
import React.Basic.Events (merge)
1111

1212
component :: Component Props
1313
component = createComponent "ControlledInput"
1414

1515
type Props = Unit
1616

17-
data Action
18-
= ValueChanged String Number
19-
2017
controlledInput :: Props -> JSX
2118
controlledInput = make component
2219
{ initialState:
2320
{ value: "hello world"
2421
, timestamp: Nothing
2522
}
2623

27-
, update: \self -> case _ of
28-
ValueChanged value timestamp ->
29-
Update self.state
30-
{ value = value
31-
, timestamp = Just timestamp
32-
}
33-
3424
, render: \self ->
3525
React.fragment
3626
[ R.input
3727
{ onChange:
38-
capture self (merge { targetValue, timeStamp })
39-
\{ timeStamp, targetValue } -> ValueChanged (fromMaybe "" targetValue) timeStamp
28+
capture (merge { targetValue, timeStamp })
29+
\{ timeStamp, targetValue } ->
30+
self.setState _
31+
{ value = fromMaybe "" targetValue
32+
, timestamp = Just timeStamp
33+
}
4034
, value: self.state.value
4135
}
4236
, R.p_ [ R.text ("Current value = " <> show self.state.value) ]

Diff for: examples/counter/src/Counter.purs

+4-10
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ module Counter where
22

33
import Prelude
44

5-
import React.Basic (Component, JSX, StateUpdate(..), capture_, createComponent, make)
5+
import React.Basic (Component, JSX, createComponent, make)
66
import React.Basic.DOM as R
7+
import React.Basic.DOM.Events (capture_)
78

89
component :: Component Props
910
component = createComponent "Counter"
@@ -12,20 +13,13 @@ type Props =
1213
{ label :: String
1314
}
1415

15-
data Action
16-
= Increment
17-
1816
counter :: Props -> JSX
19-
counter = make component { initialState, update, render }
17+
counter = make component { initialState, render }
2018
where
2119
initialState = { counter: 0 }
2220

23-
update self = case _ of
24-
Increment ->
25-
Update self.state { counter = self.state.counter + 1 }
26-
2721
render self =
2822
R.button
29-
{ onClick: capture_ self Increment
23+
{ onClick: capture_ $ self.setState \s -> s { counter = s.counter + 1 }
3024
, children: [ R.text (self.props.label <> ": " <> show self.state.counter) ]
3125
}

0 commit comments

Comments
 (0)