Skip to content

Commit c1b7bfb

Browse files
authored
Add Components*ReactHooks recipes (#249)
* Add ComponentsReactHooks * Add ComponentsInputReactHooks * Add ComponentsMultiTypeReactHooks * Update README * Add render count to ComponentsReactHooks * Add render count to ComponentsInputReactHooks * Add render count to ComponentsMultiTypeReactHooks * Add render count to ComponentsHalogenHooks * Add render count to ComponentsInputHalogenHooks * Add render count to ComponentsMultiTypeHalogenHooks * s/re-rendered/rendered
1 parent 47b1a09 commit c1b7bfb

File tree

25 files changed

+559
-3
lines changed

25 files changed

+559
-3
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,10 @@ Running a web-compatible recipe:
104104
| | :heavy_check_mark: ([try](https://try.ps.ai/?github=JordanMartinez/purescript-cookbook/master/recipes/ClockReactHooks/src/Main.purs)) | [ClockReactHooks](recipes/ClockReactHooks) | A React port of the ["User Interface - Clock" Elm Example](https://elm-lang.org/examples/clock). |
105105
| | :heavy_check_mark: ([try](https://try.ps.ai/?github=JordanMartinez/purescript-cookbook/master/recipes/ComponentsHalogenHooks/src/Main.purs)) | [ComponentsHalogenHooks](recipes/ComponentsHalogenHooks) | Demonstrates how to nest one Halogen-Hooks-based component inside another and send/receive queries between the two. |
106106
| | :heavy_check_mark: ([try](https://try.ps.ai/?github=JordanMartinez/purescript-cookbook/master/recipes/ComponentsInputHalogenHooks/src/Main.purs)) | [ComponentsInputHalogenHooks](recipes/ComponentsInputHalogenHooks) | Each time a parent re-renders, it will pass a new input value into the child, and the child will update accordingly. |
107+
| | :heavy_check_mark: ([try](https://try.ps.ai/?github=JordanMartinez/purescript-cookbook/master/recipes/ComponentsInputReactHooks/src/Main.purs)) | [ComponentsInputReactHooks](recipes/ComponentsInputReactHooks) | Each time the parent's state updates, it will pass a new prop value into the child, and the child will update accordingly. |
107108
| | :heavy_check_mark: ([try](https://try.ps.ai/?github=JordanMartinez/purescript-cookbook/master/recipes/ComponentsMultiTypeHalogenHooks/src/Main.purs)) | [ComponentsMultiTypeHalogenHooks](recipes/ComponentsMultiTypeHalogenHooks) | Demonstrates a component that can communicate with its children that have differing types. |
109+
| | :heavy_check_mark: ([try](https://try.ps.ai/?github=JordanMartinez/purescript-cookbook/master/recipes/ComponentsMultiTypeReactHooks/src/Main.purs)) | [ComponentsMultiTypeReactHooks](recipes/ComponentsMultiTypeReactHooks) | Demonstrates a parent component with several children components, each with different prop types. |
110+
| | :heavy_check_mark: ([try](https://try.ps.ai/?github=JordanMartinez/purescript-cookbook/master/recipes/ComponentsReactHooks/src/Main.purs)) | [ComponentsReactHooks](recipes/ComponentsReactHooks) | Demonstrates how to nest one React Hooks-based component inside another and send props from the parent to the child component. |
108111
| :heavy_check_mark: | :heavy_check_mark: ([try](https://try.ps.ai/?github=JordanMartinez/purescript-cookbook/master/recipes/DateTimeBasicsLog/src/Main.purs)) | [DateTimeBasicsLog](recipes/DateTimeBasicsLog) | This recipe shows how to use `purescript-datetime` library to create `Time`, `Date`, and `DateTime` values and adjust/diff them. |
109112
| :heavy_check_mark: | :heavy_check_mark: ([try](https://try.ps.ai/?github=JordanMartinez/purescript-cookbook/master/recipes/DebuggingLog/src/Main.purs)) | [DebuggingLog](recipes/DebuggingLog) | This recipe shows how to do print-debugging using the `Debug` module's `spy` and `traceM` functions. The compiler will emit warnings to remind you to remove these debug functions before you ship production code. |
110113
| :heavy_check_mark: | | [DiceCLI](recipes/DiceCLI) | This recipe shows how to create an interactive command line prompt that repeatedly generates a random number between 1 and 6. |

recipes/ComponentsHalogenHooks/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ Demonstrates how to nest one Halogen-Hooks-based component inside another and se
77
### Browser
88

99
Clicking the "On/Off" button in the child will toggle the button's label and update the parent component's toggle count. If the user clicks the "Check now" button in the parent, the parent will update its cache of the child's label state.
10+
The parent will also display how many times it has been rendered.

recipes/ComponentsHalogenHooks/src/Main.purs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@ import Data.Maybe (Maybe(..), maybe)
66
import Data.Symbol (SProxy(..))
77
import Data.Tuple.Nested ((/\))
88
import Effect (Effect)
9+
import Effect.Class (class MonadEffect)
10+
import Effect.Ref as Ref
11+
import Halogen (liftEffect)
912
import Halogen as H
1013
import Halogen.Aff as HA
1114
import Halogen.HTML as HH
1215
import Halogen.HTML.Events as HE
1316
import Halogen.HTML.Properties as HP
17+
import Halogen.Hooks (Hooked, UseEffect, UseRef)
1418
import Halogen.Hooks as Hooks
1519
import Halogen.VDom.Driver (runUI)
1620

@@ -25,8 +29,10 @@ _button = SProxy
2529

2630
containerComponent
2731
:: forall unusedQuery unusedInput unusedOutput anyMonad
28-
. H.Component HH.HTML unusedQuery unusedInput unusedOutput anyMonad
32+
. MonadEffect anyMonad
33+
=> H.Component HH.HTML unusedQuery unusedInput unusedOutput anyMonad
2934
containerComponent = Hooks.component \rec _ -> Hooks.do
35+
parentRenders <- useRenderCount
3036
toggleCount /\ toggleCountIdx <- Hooks.useState 0
3137
buttonState /\ buttonStateIdx <- Hooks.useState (Nothing :: Maybe Boolean)
3238
Hooks.pure $
@@ -47,6 +53,8 @@ containerComponent = Hooks.component \rec _ -> Hooks.do
4753
]
4854
[ HH.text "Check now" ]
4955
]
56+
, HH.p_
57+
[ HH.text ("Parent has been rendered " <> show parentRenders <> " time(s)") ]
5058
]
5159

5260
data ButtonMessage = Toggled Boolean
@@ -70,3 +78,14 @@ buttonComponent = Hooks.component \rec _ -> Hooks.do
7078
Hooks.raise rec.outputToken $ Toggled newState
7179
]
7280
[ HH.text label ]
81+
82+
useRenderCount
83+
:: forall m a
84+
. MonadEffect m
85+
=> Hooked m a (UseEffect (UseRef Int a)) Int
86+
useRenderCount = Hooks.do
87+
renders /\ rendersRef <- Hooks.useRef 1
88+
Hooks.captures {} Hooks.useTickEffect do
89+
liftEffect (Ref.modify_ (_ + 1) rendersRef)
90+
mempty
91+
Hooks.pure renders

recipes/ComponentsInputHalogenHooks/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ Each time a parent re-renders, it will pass a new input value into the child, an
77
### Browser
88

99
The parent stores an `Int`. Clicking the buttons will increment/decrement that value. The children will produce the result of a mathematical computation using that value.
10+
The parent will also display how many times it has been rendered.

recipes/ComponentsInputHalogenHooks/src/Main.purs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import Data.Maybe (Maybe(..))
66
import Data.Symbol (SProxy(..))
77
import Data.Tuple.Nested ((/\))
88
import Effect (Effect)
9-
import Effect.Class (class MonadEffect)
9+
import Effect.Class (class MonadEffect, liftEffect)
10+
import Effect.Ref as Ref
1011
import Halogen as H
1112
import Halogen.Aff as HA
1213
import Halogen.HTML as HH
1314
import Halogen.HTML.Events as HE
15+
import Halogen.Hooks (Hooked, UseEffect, UseRef)
1416
import Halogen.Hooks as Hooks
1517
import Halogen.VDom.Driver (runUI)
1618

@@ -28,6 +30,7 @@ containerComponent
2830
. MonadEffect anyMonad
2931
=> H.Component HH.HTML unusedQuery unusedInput unusedOutput anyMonad
3032
containerComponent = Hooks.component \_ _ -> Hooks.do
33+
parentRenders <- useRenderCount
3134
state /\ stateIdx <- Hooks.useState 0
3235
Hooks.pure $
3336
HH.div_
@@ -44,6 +47,8 @@ containerComponent = Hooks.component \_ _ -> Hooks.do
4447
, HH.button
4548
[ HE.onClick \_ -> Just $ Hooks.modify_ stateIdx (_ - 1) ]
4649
[ HH.text "-1"]
50+
, HH.p_
51+
[ HH.text ("Parent has been rendered " <> show parentRenders <> " time(s)") ]
4752
]
4853

4954
_display = SProxy :: SProxy "display"
@@ -58,3 +63,14 @@ displayComponent = Hooks.component \_ input -> Hooks.do
5863
[ HH.text "My input value is:"
5964
, HH.strong_ [ HH.text (show input) ]
6065
]
66+
67+
useRenderCount
68+
:: forall m a
69+
. MonadEffect m
70+
=> Hooked m a (UseEffect (UseRef Int a)) Int
71+
useRenderCount = Hooks.do
72+
renders /\ rendersRef <- Hooks.useRef 1
73+
Hooks.captures {} Hooks.useTickEffect do
74+
liftEffect (Ref.modify_ (_ + 1) rendersRef)
75+
mempty
76+
Hooks.pure renders
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/bower_components/
2+
/node_modules/
3+
/.pulp-cache/
4+
/output/
5+
/generated-docs/
6+
/.psc-package/
7+
/.psc*
8+
/.purs*
9+
/.psa*
10+
/.spago
11+
/.cache/
12+
/dist/
13+
/web-dist/
14+
/prod-dist/
15+
/prod/
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# ComponentsInputReactHooks
2+
3+
Each time the parent's state updates, it will pass a new prop value into the child, and the child will update accordingly.
4+
5+
## Expected Behavior:
6+
7+
### Browser
8+
9+
The parent stores an `Int` as state. Clicking the buttons will increment/decrement that value. The children will display the result of a mathematical computation using that value.
10+
The parent will also display how many times it has been rendered.
11+
12+
## Dependencies Used:
13+
14+
[react](https://www.npmjs.com/package/react)
15+
[react-dom](https://www.npmjs.com/package/react-dom)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{ name = "ComponentsInputReactHooks"
2+
, dependencies =
3+
[ "console"
4+
, "effect"
5+
, "psci-support"
6+
, "react-basic-hooks"
7+
, "react-basic-dom"
8+
]
9+
, packages = ../../packages.dhall
10+
, sources = [ "recipes/ComponentsInputReactHooks/src/**/*.purs" ]
11+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
module ComponentsInputReactHooks.Main where
2+
3+
import Prelude
4+
import Data.Maybe (Maybe(..))
5+
import Effect (Effect)
6+
import Effect.Exception (throw)
7+
import Effect.Unsafe (unsafePerformEffect)
8+
import React.Basic.DOM (render)
9+
import React.Basic.DOM as R
10+
import React.Basic.Events (handler_)
11+
import React.Basic.Hooks
12+
( Component
13+
, Render
14+
, UseEffect
15+
, UseRef
16+
, component
17+
, readRef
18+
, useEffectAlways
19+
, useRef
20+
, useState
21+
, writeRef
22+
, (/\)
23+
)
24+
import React.Basic.Hooks as React
25+
import Web.HTML (window)
26+
import Web.HTML.HTMLDocument (body)
27+
import Web.HTML.HTMLElement (toElement)
28+
import Web.HTML.Window (document)
29+
30+
main :: Effect Unit
31+
main = do
32+
body <- body =<< document =<< window
33+
case body of
34+
Nothing -> throw "Could not find body."
35+
Just b -> do
36+
container <- mkContainer
37+
render (container unit) (toElement b)
38+
39+
mkContainer :: Component Unit
40+
mkContainer = do
41+
display <- mkDisplay
42+
component "Container" \_ -> React.do
43+
parentRenders <- useRenderCount
44+
state /\ setState <- useState 0
45+
pure
46+
$ R.div_
47+
[ R.ul_
48+
[ display state
49+
, display (state * 2)
50+
, display (state * 3)
51+
, display (state * 10)
52+
, display (state * state)
53+
]
54+
, R.button
55+
{ onClick: handler_ (setState (_ + 1))
56+
, children: [ R.text "+1" ]
57+
}
58+
, R.button
59+
{ onClick: handler_ (setState (_ - 1))
60+
, children: [ R.text "-1" ]
61+
}
62+
, R.p_
63+
[ R.text ("Parent has been rendered " <> show parentRenders <> " time(s)") ]
64+
]
65+
66+
mkDisplay :: Component Int
67+
mkDisplay =
68+
component "Display" \n ->
69+
pure
70+
$ R.div_
71+
[ R.text "My input value is: "
72+
, R.strong_ [ R.text (show n) ]
73+
]
74+
75+
useRenderCount :: forall a. Render a (UseEffect Unit (UseRef Int a)) Int
76+
useRenderCount = React.do
77+
rendersRef <- useRef 1
78+
useEffectAlways do
79+
renders <- readRef rendersRef
80+
writeRef rendersRef (renders + 1)
81+
pure mempty
82+
let
83+
renders = unsafePerformEffect (readRef rendersRef)
84+
pure renders
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>ComponentsInputReactHooks</title>
6+
</head>
7+
8+
<body>
9+
<div id="root"></div>
10+
<script src="./index.js"></script>
11+
</body>
12+
</html>

0 commit comments

Comments
 (0)