From d66c9a36deebdddaa87b53498f3af9c34cc81feb Mon Sep 17 00:00:00 2001 From: Peter Murphy Date: Sat, 5 Dec 2020 17:00:51 -0500 Subject: [PATCH 01/11] Add ComponentsReactHooks --- recipes/ComponentsReactHooks/.gitignore | 15 +++++ recipes/ComponentsReactHooks/README.md | 14 +++++ recipes/ComponentsReactHooks/spago.dhall | 11 ++++ recipes/ComponentsReactHooks/src/Main.purs | 65 +++++++++++++++++++++ recipes/ComponentsReactHooks/web/index.html | 12 ++++ recipes/ComponentsReactHooks/web/index.js | 2 + 6 files changed, 119 insertions(+) create mode 100644 recipes/ComponentsReactHooks/.gitignore create mode 100644 recipes/ComponentsReactHooks/README.md create mode 100644 recipes/ComponentsReactHooks/spago.dhall create mode 100644 recipes/ComponentsReactHooks/src/Main.purs create mode 100644 recipes/ComponentsReactHooks/web/index.html create mode 100644 recipes/ComponentsReactHooks/web/index.js diff --git a/recipes/ComponentsReactHooks/.gitignore b/recipes/ComponentsReactHooks/.gitignore new file mode 100644 index 00000000..57030e7b --- /dev/null +++ b/recipes/ComponentsReactHooks/.gitignore @@ -0,0 +1,15 @@ +/bower_components/ +/node_modules/ +/.pulp-cache/ +/output/ +/generated-docs/ +/.psc-package/ +/.psc* +/.purs* +/.psa* +/.spago +/.cache/ +/dist/ +/web-dist/ +/prod-dist/ +/prod/ diff --git a/recipes/ComponentsReactHooks/README.md b/recipes/ComponentsReactHooks/README.md new file mode 100644 index 00000000..56ea0f3c --- /dev/null +++ b/recipes/ComponentsReactHooks/README.md @@ -0,0 +1,14 @@ +# ComponentsReactHooks + +Demonstrates how to nest one React Hooks-based component inside another and send props from the parent to the child component. + +## Expected Behavior: + +### Browser + +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 button state. + +## Dependencies Used: + +[react](https://www.npmjs.com/package/react) +[react-dom](https://www.npmjs.com/package/react-dom) diff --git a/recipes/ComponentsReactHooks/spago.dhall b/recipes/ComponentsReactHooks/spago.dhall new file mode 100644 index 00000000..f217e4e7 --- /dev/null +++ b/recipes/ComponentsReactHooks/spago.dhall @@ -0,0 +1,11 @@ +{ name = "ComponentsReactHooks" +, dependencies = + [ "console" + , "effect" + , "psci-support" + , "react-basic-hooks" + , "react-basic-dom" + ] +, packages = ../../packages.dhall +, sources = [ "recipes/ComponentsReactHooks/src/**/*.purs" ] +} diff --git a/recipes/ComponentsReactHooks/src/Main.purs b/recipes/ComponentsReactHooks/src/Main.purs new file mode 100644 index 00000000..134dbe16 --- /dev/null +++ b/recipes/ComponentsReactHooks/src/Main.purs @@ -0,0 +1,65 @@ +module ComponentsReactHooks.Main where + +import Prelude +import Data.Maybe (Maybe(..), maybe) +import Effect (Effect) +import Effect.Exception (throw) +import React.Basic.DOM (render) +import React.Basic.DOM as R +import React.Basic.Events (EventHandler, handler_) +import React.Basic.Hooks (Component, component, useState, (/\)) +import React.Basic.Hooks as React +import Web.HTML (window) +import Web.HTML.HTMLDocument (body) +import Web.HTML.HTMLElement (toElement) +import Web.HTML.Window (document) + +main :: Effect Unit +main = do + body <- body =<< document =<< window + case body of + Nothing -> throw "Could not find body." + Just b -> do + container <- mkContainer + render (container unit) (toElement b) + +mkContainer :: Component Unit +mkContainer = do + button <- mkButton + component "Container" \_ -> React.do + count /\ setCount <- useState 0 + enabled /\ setEnabled <- useState false + buttonState /\ setButtonState <- useState Nothing + let + handleClick = + handler_ do + setCount (_ + 1) + setEnabled not + pure + $ R.div_ + [ button { enabled, handleClick } + , R.p_ + [ R.text ("Button has been toggled " <> show count <> " time(s)") ] + , R.p_ + [ R.text + $ "Last time I checked, the button was: " + <> (maybe "(not checked yet)" (if _ then "on" else "off") buttonState) + <> ". " + , R.button + { onClick: handler_ (setButtonState \_ -> Just enabled) + , children: [ R.text "Check now" ] + } + ] + ] + +mkButton :: Component { enabled :: Boolean, handleClick :: EventHandler } +mkButton = + component "Button" \props -> React.do + let + label = if props.enabled then "On" else "Off" + pure + $ R.button + { title: label + , onClick: props.handleClick + , children: [ R.text label ] + } diff --git a/recipes/ComponentsReactHooks/web/index.html b/recipes/ComponentsReactHooks/web/index.html new file mode 100644 index 00000000..0d7f1885 --- /dev/null +++ b/recipes/ComponentsReactHooks/web/index.html @@ -0,0 +1,12 @@ + + + + + ComponentsReactHooks + + + +
+ + + diff --git a/recipes/ComponentsReactHooks/web/index.js b/recipes/ComponentsReactHooks/web/index.js new file mode 100644 index 00000000..bb0e041e --- /dev/null +++ b/recipes/ComponentsReactHooks/web/index.js @@ -0,0 +1,2 @@ +"use strict"; +require("../../../output/ComponentsReactHooks.Main/index.js").main(); From 1644a08dac918d22ebd3e2e1951a09d72a2a2b88 Mon Sep 17 00:00:00 2001 From: Peter Murphy Date: Sat, 5 Dec 2020 20:19:00 -0500 Subject: [PATCH 02/11] Add ComponentsInputReactHooks --- recipes/ComponentsInputReactHooks/.gitignore | 15 +++++ recipes/ComponentsInputReactHooks/README.md | 14 +++++ recipes/ComponentsInputReactHooks/spago.dhall | 11 ++++ .../ComponentsInputReactHooks/src/Main.purs | 57 +++++++++++++++++++ .../ComponentsInputReactHooks/web/index.html | 12 ++++ .../ComponentsInputReactHooks/web/index.js | 2 + 6 files changed, 111 insertions(+) create mode 100644 recipes/ComponentsInputReactHooks/.gitignore create mode 100644 recipes/ComponentsInputReactHooks/README.md create mode 100644 recipes/ComponentsInputReactHooks/spago.dhall create mode 100644 recipes/ComponentsInputReactHooks/src/Main.purs create mode 100644 recipes/ComponentsInputReactHooks/web/index.html create mode 100644 recipes/ComponentsInputReactHooks/web/index.js diff --git a/recipes/ComponentsInputReactHooks/.gitignore b/recipes/ComponentsInputReactHooks/.gitignore new file mode 100644 index 00000000..57030e7b --- /dev/null +++ b/recipes/ComponentsInputReactHooks/.gitignore @@ -0,0 +1,15 @@ +/bower_components/ +/node_modules/ +/.pulp-cache/ +/output/ +/generated-docs/ +/.psc-package/ +/.psc* +/.purs* +/.psa* +/.spago +/.cache/ +/dist/ +/web-dist/ +/prod-dist/ +/prod/ diff --git a/recipes/ComponentsInputReactHooks/README.md b/recipes/ComponentsInputReactHooks/README.md new file mode 100644 index 00000000..96b7a348 --- /dev/null +++ b/recipes/ComponentsInputReactHooks/README.md @@ -0,0 +1,14 @@ +# ComponentsInputReactHooks + +Each time the parent's state updates, it will pass a new prop value into the child, and the child will update accordingly. + +## Expected Behavior: + +### Browser + +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. + +## Dependencies Used: + +[react](https://www.npmjs.com/package/react) +[react-dom](https://www.npmjs.com/package/react-dom) diff --git a/recipes/ComponentsInputReactHooks/spago.dhall b/recipes/ComponentsInputReactHooks/spago.dhall new file mode 100644 index 00000000..8be6669d --- /dev/null +++ b/recipes/ComponentsInputReactHooks/spago.dhall @@ -0,0 +1,11 @@ +{ name = "ComponentsInputReactHooks" +, dependencies = + [ "console" + , "effect" + , "psci-support" + , "react-basic-hooks" + , "react-basic-dom" + ] +, packages = ../../packages.dhall +, sources = [ "recipes/ComponentsInputReactHooks/src/**/*.purs" ] +} diff --git a/recipes/ComponentsInputReactHooks/src/Main.purs b/recipes/ComponentsInputReactHooks/src/Main.purs new file mode 100644 index 00000000..1d97c388 --- /dev/null +++ b/recipes/ComponentsInputReactHooks/src/Main.purs @@ -0,0 +1,57 @@ +module ComponentsInputReactHooks.Main where + +import Prelude +import Data.Maybe (Maybe(..)) +import Effect (Effect) +import Effect.Exception (throw) +import React.Basic.DOM (render) +import React.Basic.DOM as R +import React.Basic.Events (handler_) +import React.Basic.Hooks (Component, component, useState, (/\)) +import React.Basic.Hooks as React +import Web.HTML (window) +import Web.HTML.HTMLDocument (body) +import Web.HTML.HTMLElement (toElement) +import Web.HTML.Window (document) + +main :: Effect Unit +main = do + body <- body =<< document =<< window + case body of + Nothing -> throw "Could not find body." + Just b -> do + container <- mkContainer + render (container unit) (toElement b) + +mkContainer :: Component Unit +mkContainer = do + display <- mkDisplay + component "Container" \_ -> React.do + state /\ setState <- useState 0 + pure + $ R.div_ + [ R.ul_ + [ display state + , display (state * 2) + , display (state * 3) + , display (state * 10) + , display (state * state) + ] + , R.button + { onClick: handler_ (setState (_ + 1)) + , children: [ R.text "+1" ] + } + , R.button + { onClick: handler_ (setState (_ - 1)) + , children: [ R.text "-1" ] + } + ] + +mkDisplay :: Component Int +mkDisplay = + component "Display" \n -> + pure + $ R.div_ + [ R.text "My input value is: " + , R.strong_ [ R.text (show n) ] + ] diff --git a/recipes/ComponentsInputReactHooks/web/index.html b/recipes/ComponentsInputReactHooks/web/index.html new file mode 100644 index 00000000..bfbb931a --- /dev/null +++ b/recipes/ComponentsInputReactHooks/web/index.html @@ -0,0 +1,12 @@ + + + + + ComponentsInputReactHooks + + + +
+ + + diff --git a/recipes/ComponentsInputReactHooks/web/index.js b/recipes/ComponentsInputReactHooks/web/index.js new file mode 100644 index 00000000..3d77948a --- /dev/null +++ b/recipes/ComponentsInputReactHooks/web/index.js @@ -0,0 +1,2 @@ +"use strict"; +require("../../../output/ComponentsInputReactHooks.Main/index.js").main(); From 87334711a02724b0c8cd64eff4e3cb25a577e217 Mon Sep 17 00:00:00 2001 From: Peter Murphy Date: Sat, 5 Dec 2020 21:38:18 -0500 Subject: [PATCH 03/11] Add ComponentsMultiTypeReactHooks --- .../ComponentsMultiTypeReactHooks/.gitignore | 15 ++ .../ComponentsMultiTypeReactHooks/README.md | 14 ++ .../ComponentsMultiTypeReactHooks/spago.dhall | 11 ++ .../src/Main.purs | 134 ++++++++++++++++++ .../web/index.html | 12 ++ .../web/index.js | 2 + 6 files changed, 188 insertions(+) create mode 100644 recipes/ComponentsMultiTypeReactHooks/.gitignore create mode 100644 recipes/ComponentsMultiTypeReactHooks/README.md create mode 100644 recipes/ComponentsMultiTypeReactHooks/spago.dhall create mode 100644 recipes/ComponentsMultiTypeReactHooks/src/Main.purs create mode 100644 recipes/ComponentsMultiTypeReactHooks/web/index.html create mode 100644 recipes/ComponentsMultiTypeReactHooks/web/index.js diff --git a/recipes/ComponentsMultiTypeReactHooks/.gitignore b/recipes/ComponentsMultiTypeReactHooks/.gitignore new file mode 100644 index 00000000..57030e7b --- /dev/null +++ b/recipes/ComponentsMultiTypeReactHooks/.gitignore @@ -0,0 +1,15 @@ +/bower_components/ +/node_modules/ +/.pulp-cache/ +/output/ +/generated-docs/ +/.psc-package/ +/.psc* +/.purs* +/.psa* +/.spago +/.cache/ +/dist/ +/web-dist/ +/prod-dist/ +/prod/ diff --git a/recipes/ComponentsMultiTypeReactHooks/README.md b/recipes/ComponentsMultiTypeReactHooks/README.md new file mode 100644 index 00000000..8cb6034b --- /dev/null +++ b/recipes/ComponentsMultiTypeReactHooks/README.md @@ -0,0 +1,14 @@ +# ComponentsMultiTypeReactHooks + +Demonstrates a parent component with several children components, each with different prop types. + +## Expected Behavior: + +### Browser + +A toggle button, a count button, and an input field are the children of the parent. The user can modify the state of any of those inputs and then click the "Check now" button. This will update the most recent observed state. + +## Dependencies Used: + +[react](https://www.npmjs.com/package/react) +[react-dom](https://www.npmjs.com/package/react-dom) diff --git a/recipes/ComponentsMultiTypeReactHooks/spago.dhall b/recipes/ComponentsMultiTypeReactHooks/spago.dhall new file mode 100644 index 00000000..e6b4b2e9 --- /dev/null +++ b/recipes/ComponentsMultiTypeReactHooks/spago.dhall @@ -0,0 +1,11 @@ +{ name = "ComponentsMultiTypeReactHooks" +, dependencies = + [ "console" + , "effect" + , "psci-support" + , "react-basic-hooks" + , "react-basic-dom" + ] +, packages = ../../packages.dhall +, sources = [ "recipes/ComponentsMultiTypeReactHooks/src/**/*.purs" ] +} diff --git a/recipes/ComponentsMultiTypeReactHooks/src/Main.purs b/recipes/ComponentsMultiTypeReactHooks/src/Main.purs new file mode 100644 index 00000000..2c119075 --- /dev/null +++ b/recipes/ComponentsMultiTypeReactHooks/src/Main.purs @@ -0,0 +1,134 @@ +module ComponentsMultiTypeReactHooks.Main where + +import Prelude +import Data.Maybe (Maybe(..), fromMaybe) +import Data.Tuple (fst) +import Effect (Effect) +import Effect.Exception (throw) +import React.Basic.DOM (render) +import React.Basic.DOM as R +import React.Basic.DOM.Events (targetValue) +import React.Basic.Events (EventHandler, handler, handler_) +import React.Basic.Hooks + ( Component + , Hook + , UseState + , component + , useState + , useState' + , (/\) + , type (/\) + ) +import React.Basic.Hooks as React +import Web.HTML (window) +import Web.HTML.HTMLDocument (body) +import Web.HTML.HTMLElement (toElement) +import Web.HTML.Window (document) + +main :: Effect Unit +main = do + body <- body =<< document =<< window + case body of + Nothing -> throw "Could not find body." + Just b -> do + container <- mkContainer + render (container unit) (toElement b) + +mkContainer :: Component Unit +mkContainer = do + componentA <- mkComponentA + componentB <- mkComponentB + componentC <- mkComponentC + component "Container" \_ -> React.do + observedState /\ setObservedState <- + useState' { a: Nothing, b: Nothing, c: Nothing } + enabledState <- useState false + countState <- useState 0 + inputState <- useInput "Hello" + pure + $ R.div_ + [ R.div + { className: "box" + , children: + [ R.h1_ [ R.text "Component A" ] + , componentA enabledState + ] + } + , R.div + { className: "box" + , children: + [ R.h1_ [ R.text "Component B" ] + , componentB countState + ] + } + , R.div + { className: "box" + , children: + [ R.h1_ [ R.text "Component C" ] + , componentC inputState + ] + } + , R.p_ + [ R.text "Last observed states:" ] + , R.ul_ + [ R.li_ [ R.text ("Component A: " <> show observedState.a) ] + , R.li_ [ R.text ("Component B: " <> show observedState.b) ] + , R.li_ [ R.text ("Component C: " <> show observedState.c) ] + ] + , R.button + { onClick: + handler_ do + setObservedState + { a: Just (fst enabledState) + , b: Just (fst countState) + , c: Just (fst inputState) + } + , children: [ R.text "Check states now" ] + } + ] + +type SetState state + = (state -> state) -> Effect Unit + +mkComponentA :: Component (Boolean /\ SetState Boolean) +mkComponentA = + component "Component A" \(enabled /\ setEnabled) -> + pure + $ R.div_ + [ R.p_ [ R.text "Toggle me!" ] + , R.button + { onClick: handler_ (setEnabled not) + , children: [ R.text (if enabled then "On" else "Off") ] + } + ] + +mkComponentB :: Component (Int /\ SetState Int) +mkComponentB = + component "Component B" \(count /\ setCount) -> + pure + $ R.div_ + [ R.p_ + [ R.text "Current value: " + , R.strong_ [ R.text (show count) ] + ] + , R.button + { onClick: handler_ (setCount (_ + 1)) + , children: [ R.text "Increment" ] + } + ] + +mkComponentC :: Component (String /\ EventHandler) +mkComponentC = + component "Component C" \(value /\ onChange) -> + pure + $ R.label_ + [ R.p_ [ R.text "What do you have to say?" ] + , R.input { value, onChange } + ] + +useInput :: String -> Hook (UseState String) (String /\ EventHandler) +useInput initialValue = React.do + state /\ setState <- useState' initialValue + let + onChange = handler targetValue \t -> setState (fromMaybe "" t) + pure (state /\ onChange) diff --git a/recipes/ComponentsMultiTypeReactHooks/web/index.html b/recipes/ComponentsMultiTypeReactHooks/web/index.html new file mode 100644 index 00000000..12f58f91 --- /dev/null +++ b/recipes/ComponentsMultiTypeReactHooks/web/index.html @@ -0,0 +1,12 @@ + + + + + ComponentsMultiTypeReactHooks + + + +
+ + + diff --git a/recipes/ComponentsMultiTypeReactHooks/web/index.js b/recipes/ComponentsMultiTypeReactHooks/web/index.js new file mode 100644 index 00000000..c92f7865 --- /dev/null +++ b/recipes/ComponentsMultiTypeReactHooks/web/index.js @@ -0,0 +1,2 @@ +"use strict"; +require("../../../output/ComponentsMultiTypeReactHooks.Main/index.js").main(); From 7cb13d4fccd043f4fb9468e2e19af54b0e070533 Mon Sep 17 00:00:00 2001 From: Peter Murphy Date: Sat, 5 Dec 2020 23:29:18 -0500 Subject: [PATCH 04/11] Update README --- README.md | 3 +++ recipes/ComponentsMultiTypeReactHooks/src/Main.purs | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 119f34d2..d9e3aa76 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,10 @@ Running a web-compatible recipe: | | :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). | | | :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. | | | :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. | +| | :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. | | | :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. | +| | :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. | +| | :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. | | :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. | | :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. | | :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. | diff --git a/recipes/ComponentsMultiTypeReactHooks/src/Main.purs b/recipes/ComponentsMultiTypeReactHooks/src/Main.purs index 2c119075..f623446c 100644 --- a/recipes/ComponentsMultiTypeReactHooks/src/Main.purs +++ b/recipes/ComponentsMultiTypeReactHooks/src/Main.purs @@ -92,7 +92,7 @@ type SetState state mkComponentA :: Component (Boolean /\ SetState Boolean) mkComponentA = - component "Component A" \(enabled /\ setEnabled) -> + component "ComponentA" \(enabled /\ setEnabled) -> pure $ R.div_ [ R.p_ [ R.text "Toggle me!" ] @@ -104,7 +104,7 @@ mkComponentA = mkComponentB :: Component (Int /\ SetState Int) mkComponentB = - component "Component B" \(count /\ setCount) -> + component "ComponentB" \(count /\ setCount) -> pure $ R.div_ [ R.p_ @@ -119,7 +119,7 @@ mkComponentB = mkComponentC :: Component (String /\ EventHandler) mkComponentC = - component "Component C" \(value /\ onChange) -> + component "ComponentC" \(value /\ onChange) -> pure $ R.label_ [ R.p_ [ R.text "What do you have to say?" ] From 46d4ea0f7b7e0d07f8f98d20c356e6f2a20b5f60 Mon Sep 17 00:00:00 2001 From: Peter Murphy <26548438+ptrfrncsmrph@users.noreply.github.com> Date: Sat, 16 Jan 2021 21:20:14 -0500 Subject: [PATCH 05/11] Add render count to ComponentsReactHooks --- recipes/ComponentsReactHooks/README.md | 1 + recipes/ComponentsReactHooks/src/Main.purs | 29 +++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/recipes/ComponentsReactHooks/README.md b/recipes/ComponentsReactHooks/README.md index 56ea0f3c..45c22c46 100644 --- a/recipes/ComponentsReactHooks/README.md +++ b/recipes/ComponentsReactHooks/README.md @@ -7,6 +7,7 @@ Demonstrates how to nest one React Hooks-based component inside another and send ### Browser 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 button state. +The parent will also display how many times it has been re-rendered. ## Dependencies Used: diff --git a/recipes/ComponentsReactHooks/src/Main.purs b/recipes/ComponentsReactHooks/src/Main.purs index 134dbe16..b389f276 100644 --- a/recipes/ComponentsReactHooks/src/Main.purs +++ b/recipes/ComponentsReactHooks/src/Main.purs @@ -4,10 +4,23 @@ import Prelude import Data.Maybe (Maybe(..), maybe) import Effect (Effect) import Effect.Exception (throw) +import Effect.Unsafe (unsafePerformEffect) import React.Basic.DOM (render) import React.Basic.DOM as R import React.Basic.Events (EventHandler, handler_) -import React.Basic.Hooks (Component, component, useState, (/\)) +import React.Basic.Hooks + ( Component + , Render + , UseEffect + , UseRef + , component + , readRef + , useEffectAlways + , useRef + , useState + , writeRef + , (/\) + ) import React.Basic.Hooks as React import Web.HTML (window) import Web.HTML.HTMLDocument (body) @@ -27,6 +40,7 @@ mkContainer :: Component Unit mkContainer = do button <- mkButton component "Container" \_ -> React.do + parentRenders <- useRenderCount count /\ setCount <- useState 0 enabled /\ setEnabled <- useState false buttonState /\ setButtonState <- useState Nothing @@ -50,6 +64,8 @@ mkContainer = do , children: [ R.text "Check now" ] } ] + , R.p_ + [ R.text ("Parent has been rendered " <> show parentRenders <> " time(s)") ] ] mkButton :: Component { enabled :: Boolean, handleClick :: EventHandler } @@ -63,3 +79,14 @@ mkButton = , onClick: props.handleClick , children: [ R.text label ] } + +useRenderCount :: forall a. Render a (UseEffect Unit (UseRef Int a)) Int +useRenderCount = React.do + rendersRef <- useRef 1 + useEffectAlways do + renders <- readRef rendersRef + writeRef rendersRef (renders + 1) + pure mempty + let + renders = unsafePerformEffect (readRef rendersRef) + pure renders From 651f15046cd78965b25f90e579c36e6b7bd4d7d3 Mon Sep 17 00:00:00 2001 From: Peter Murphy <26548438+ptrfrncsmrph@users.noreply.github.com> Date: Sat, 16 Jan 2021 21:26:09 -0500 Subject: [PATCH 06/11] Add render count to ComponentsInputReactHooks --- recipes/ComponentsInputReactHooks/README.md | 1 + .../ComponentsInputReactHooks/src/Main.purs | 29 ++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/recipes/ComponentsInputReactHooks/README.md b/recipes/ComponentsInputReactHooks/README.md index 96b7a348..49ca3a6b 100644 --- a/recipes/ComponentsInputReactHooks/README.md +++ b/recipes/ComponentsInputReactHooks/README.md @@ -7,6 +7,7 @@ Each time the parent's state updates, it will pass a new prop value into the chi ### Browser 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. +The parent will also display how many times it has been re-rendered. ## Dependencies Used: diff --git a/recipes/ComponentsInputReactHooks/src/Main.purs b/recipes/ComponentsInputReactHooks/src/Main.purs index 1d97c388..b6f8e139 100644 --- a/recipes/ComponentsInputReactHooks/src/Main.purs +++ b/recipes/ComponentsInputReactHooks/src/Main.purs @@ -4,10 +4,23 @@ import Prelude import Data.Maybe (Maybe(..)) import Effect (Effect) import Effect.Exception (throw) +import Effect.Unsafe (unsafePerformEffect) import React.Basic.DOM (render) import React.Basic.DOM as R import React.Basic.Events (handler_) -import React.Basic.Hooks (Component, component, useState, (/\)) +import React.Basic.Hooks + ( Component + , Render + , UseEffect + , UseRef + , component + , readRef + , useEffectAlways + , useRef + , useState + , writeRef + , (/\) + ) import React.Basic.Hooks as React import Web.HTML (window) import Web.HTML.HTMLDocument (body) @@ -27,6 +40,7 @@ mkContainer :: Component Unit mkContainer = do display <- mkDisplay component "Container" \_ -> React.do + parentRenders <- useRenderCount state /\ setState <- useState 0 pure $ R.div_ @@ -45,6 +59,8 @@ mkContainer = do { onClick: handler_ (setState (_ - 1)) , children: [ R.text "-1" ] } + , R.p_ + [ R.text ("Parent has been rendered " <> show parentRenders <> " time(s)") ] ] mkDisplay :: Component Int @@ -55,3 +71,14 @@ mkDisplay = [ R.text "My input value is: " , R.strong_ [ R.text (show n) ] ] + +useRenderCount :: forall a. Render a (UseEffect Unit (UseRef Int a)) Int +useRenderCount = React.do + rendersRef <- useRef 1 + useEffectAlways do + renders <- readRef rendersRef + writeRef rendersRef (renders + 1) + pure mempty + let + renders = unsafePerformEffect (readRef rendersRef) + pure renders From 3290619c41011e49e1399d24c5ec69a140741b80 Mon Sep 17 00:00:00 2001 From: Peter Murphy <26548438+ptrfrncsmrph@users.noreply.github.com> Date: Sat, 16 Jan 2021 21:42:57 -0500 Subject: [PATCH 07/11] Add render count to ComponentsMultiTypeReactHooks --- .../ComponentsMultiTypeReactHooks/README.md | 1 + .../src/Main.purs | 26 +++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/recipes/ComponentsMultiTypeReactHooks/README.md b/recipes/ComponentsMultiTypeReactHooks/README.md index 8cb6034b..5e97094f 100644 --- a/recipes/ComponentsMultiTypeReactHooks/README.md +++ b/recipes/ComponentsMultiTypeReactHooks/README.md @@ -7,6 +7,7 @@ Demonstrates a parent component with several children components, each with diff ### Browser A toggle button, a count button, and an input field are the children of the parent. The user can modify the state of any of those inputs and then click the "Check now" button. This will update the most recent observed state. +The parent will also display how many times it has been rendered. ## Dependencies Used: diff --git a/recipes/ComponentsMultiTypeReactHooks/src/Main.purs b/recipes/ComponentsMultiTypeReactHooks/src/Main.purs index f623446c..5bf5478c 100644 --- a/recipes/ComponentsMultiTypeReactHooks/src/Main.purs +++ b/recipes/ComponentsMultiTypeReactHooks/src/Main.purs @@ -5,19 +5,27 @@ import Data.Maybe (Maybe(..), fromMaybe) import Data.Tuple (fst) import Effect (Effect) import Effect.Exception (throw) +import Effect.Unsafe (unsafePerformEffect) import React.Basic.DOM (render) import React.Basic.DOM as R import React.Basic.DOM.Events (targetValue) import React.Basic.Events (EventHandler, handler, handler_) import React.Basic.Hooks - ( Component + ( type (/\) + , Component , Hook + , Render + , UseEffect + , UseRef , UseState , component + , readRef + , useEffectAlways + , useRef , useState , useState' + , writeRef , (/\) - , type (/\) ) import React.Basic.Hooks as React import Web.HTML (window) @@ -40,6 +48,7 @@ mkContainer = do componentB <- mkComponentB componentC <- mkComponentC component "Container" \_ -> React.do + parentRenders <- useRenderCount observedState /\ setObservedState <- useState' { a: Nothing, b: Nothing, c: Nothing } enabledState <- useState false @@ -85,6 +94,8 @@ mkContainer = do } , children: [ R.text "Check states now" ] } + , R.p_ + [ R.text ("Parent has been rendered " <> show parentRenders <> " time(s)") ] ] type SetState state @@ -132,3 +143,14 @@ useInput initialValue = React.do let onChange = handler targetValue \t -> setState (fromMaybe "" t) pure (state /\ onChange) + +useRenderCount :: forall a. Render a (UseEffect Unit (UseRef Int a)) Int +useRenderCount = React.do + rendersRef <- useRef 1 + useEffectAlways do + renders <- readRef rendersRef + writeRef rendersRef (renders + 1) + pure mempty + let + renders = unsafePerformEffect (readRef rendersRef) + pure renders From a217cd5a74e0350cf76ebeb16423fbb308f342bf Mon Sep 17 00:00:00 2001 From: Peter Murphy <26548438+ptrfrncsmrph@users.noreply.github.com> Date: Sat, 16 Jan 2021 21:59:29 -0500 Subject: [PATCH 08/11] Add render count to ComponentsHalogenHooks --- recipes/ComponentsHalogenHooks/README.md | 1 + recipes/ComponentsHalogenHooks/src/Main.purs | 21 +++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/recipes/ComponentsHalogenHooks/README.md b/recipes/ComponentsHalogenHooks/README.md index 5cf2a86c..6111d450 100644 --- a/recipes/ComponentsHalogenHooks/README.md +++ b/recipes/ComponentsHalogenHooks/README.md @@ -7,3 +7,4 @@ Demonstrates how to nest one Halogen-Hooks-based component inside another and se ### Browser 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. +The parent will also display how many times it has been rendered. diff --git a/recipes/ComponentsHalogenHooks/src/Main.purs b/recipes/ComponentsHalogenHooks/src/Main.purs index 0142032c..ac1cd0fd 100644 --- a/recipes/ComponentsHalogenHooks/src/Main.purs +++ b/recipes/ComponentsHalogenHooks/src/Main.purs @@ -6,11 +6,15 @@ import Data.Maybe (Maybe(..), maybe) import Data.Symbol (SProxy(..)) import Data.Tuple.Nested ((/\)) import Effect (Effect) +import Effect.Class (class MonadEffect) +import Effect.Ref as Ref +import Halogen (liftEffect) import Halogen as H import Halogen.Aff as HA import Halogen.HTML as HH import Halogen.HTML.Events as HE import Halogen.HTML.Properties as HP +import Halogen.Hooks (Hooked, UseEffect, UseRef) import Halogen.Hooks as Hooks import Halogen.VDom.Driver (runUI) @@ -25,8 +29,10 @@ _button = SProxy containerComponent :: forall unusedQuery unusedInput unusedOutput anyMonad - . H.Component HH.HTML unusedQuery unusedInput unusedOutput anyMonad + . MonadEffect anyMonad + => H.Component HH.HTML unusedQuery unusedInput unusedOutput anyMonad containerComponent = Hooks.component \rec _ -> Hooks.do + parentRenders <- useRenderCount toggleCount /\ toggleCountIdx <- Hooks.useState 0 buttonState /\ buttonStateIdx <- Hooks.useState (Nothing :: Maybe Boolean) Hooks.pure $ @@ -47,6 +53,8 @@ containerComponent = Hooks.component \rec _ -> Hooks.do ] [ HH.text "Check now" ] ] + , HH.p_ + [ HH.text ("Parent has been rendered " <> show parentRenders <> " time(s)") ] ] data ButtonMessage = Toggled Boolean @@ -70,3 +78,14 @@ buttonComponent = Hooks.component \rec _ -> Hooks.do Hooks.raise rec.outputToken $ Toggled newState ] [ HH.text label ] + +useRenderCount + :: forall m a + . MonadEffect m + => Hooked m a (UseEffect (UseRef Int a)) Int +useRenderCount = Hooks.do + renders /\ rendersRef <- Hooks.useRef 1 + Hooks.captures {} Hooks.useTickEffect do + liftEffect (Ref.modify_ (_ + 1) rendersRef) + mempty + Hooks.pure renders \ No newline at end of file From 6a6500fdaaaf70125fb396c2a6efdecf36c3cd68 Mon Sep 17 00:00:00 2001 From: Peter Murphy <26548438+ptrfrncsmrph@users.noreply.github.com> Date: Sat, 16 Jan 2021 22:05:41 -0500 Subject: [PATCH 09/11] Add render count to ComponentsInputHalogenHooks --- recipes/ComponentsInputHalogenHooks/README.md | 1 + .../ComponentsInputHalogenHooks/src/Main.purs | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/recipes/ComponentsInputHalogenHooks/README.md b/recipes/ComponentsInputHalogenHooks/README.md index 7116c6a3..a1cb1639 100644 --- a/recipes/ComponentsInputHalogenHooks/README.md +++ b/recipes/ComponentsInputHalogenHooks/README.md @@ -7,3 +7,4 @@ Each time a parent re-renders, it will pass a new input value into the child, an ### Browser 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. +The parent will also display how many times it has been rendered. diff --git a/recipes/ComponentsInputHalogenHooks/src/Main.purs b/recipes/ComponentsInputHalogenHooks/src/Main.purs index 078dec92..3ed22a7f 100644 --- a/recipes/ComponentsInputHalogenHooks/src/Main.purs +++ b/recipes/ComponentsInputHalogenHooks/src/Main.purs @@ -6,11 +6,13 @@ import Data.Maybe (Maybe(..)) import Data.Symbol (SProxy(..)) import Data.Tuple.Nested ((/\)) import Effect (Effect) -import Effect.Class (class MonadEffect) +import Effect.Class (class MonadEffect, liftEffect) +import Effect.Ref as Ref import Halogen as H import Halogen.Aff as HA import Halogen.HTML as HH import Halogen.HTML.Events as HE +import Halogen.Hooks (Hooked, UseEffect, UseRef) import Halogen.Hooks as Hooks import Halogen.VDom.Driver (runUI) @@ -28,6 +30,7 @@ containerComponent . MonadEffect anyMonad => H.Component HH.HTML unusedQuery unusedInput unusedOutput anyMonad containerComponent = Hooks.component \_ _ -> Hooks.do + parentRenders <- useRenderCount state /\ stateIdx <- Hooks.useState 0 Hooks.pure $ HH.div_ @@ -44,6 +47,8 @@ containerComponent = Hooks.component \_ _ -> Hooks.do , HH.button [ HE.onClick \_ -> Just $ Hooks.modify_ stateIdx (_ - 1) ] [ HH.text "-1"] + , HH.p_ + [ HH.text ("Parent has been rendered " <> show parentRenders <> " time(s)") ] ] _display = SProxy :: SProxy "display" @@ -58,3 +63,14 @@ displayComponent = Hooks.component \_ input -> Hooks.do [ HH.text "My input value is:" , HH.strong_ [ HH.text (show input) ] ] + +useRenderCount + :: forall m a + . MonadEffect m + => Hooked m a (UseEffect (UseRef Int a)) Int +useRenderCount = Hooks.do + renders /\ rendersRef <- Hooks.useRef 1 + Hooks.captures {} Hooks.useTickEffect do + liftEffect (Ref.modify_ (_ + 1) rendersRef) + mempty + Hooks.pure renders \ No newline at end of file From 099e56851fe2b603a494953f4d84e6bb5094daf1 Mon Sep 17 00:00:00 2001 From: Peter Murphy <26548438+ptrfrncsmrph@users.noreply.github.com> Date: Sat, 16 Jan 2021 22:09:13 -0500 Subject: [PATCH 10/11] Add render count to ComponentsMultiTypeHalogenHooks --- .../ComponentsMultiTypeHalogenHooks/README.md | 1 + .../src/Main.purs | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/recipes/ComponentsMultiTypeHalogenHooks/README.md b/recipes/ComponentsMultiTypeHalogenHooks/README.md index 7b0cf08c..e64abafe 100644 --- a/recipes/ComponentsMultiTypeHalogenHooks/README.md +++ b/recipes/ComponentsMultiTypeHalogenHooks/README.md @@ -7,3 +7,4 @@ Demonstrates a component that can communicate with its children that have differ ### Browser A toggle button, a count button, and an input field are the children of the parent. The user can modify either of those children's values and then click the "Check now" button. The parent will get the latest state from each of its children and display all states. +The parent will also display how many times it has been rendered. diff --git a/recipes/ComponentsMultiTypeHalogenHooks/src/Main.purs b/recipes/ComponentsMultiTypeHalogenHooks/src/Main.purs index 635eb6fa..60508b63 100644 --- a/recipes/ComponentsMultiTypeHalogenHooks/src/Main.purs +++ b/recipes/ComponentsMultiTypeHalogenHooks/src/Main.purs @@ -6,11 +6,14 @@ import Data.Maybe (Maybe(..)) import Data.Symbol (SProxy(..)) import Data.Tuple.Nested ((/\)) import Effect (Effect) +import Effect.Class (class MonadEffect, liftEffect) +import Effect.Ref as Ref import Halogen as H import Halogen.Aff as HA import Halogen.HTML as HH import Halogen.HTML.Events as HE import Halogen.HTML.Properties as HP +import Halogen.Hooks (Hooked, UseEffect, UseRef) import Halogen.Hooks as Hooks import Halogen.VDom.Driver (runUI) @@ -25,8 +28,10 @@ _button = SProxy containerComponent :: forall unusedQuery unusedInput unusedOutput anyMonad - . H.Component HH.HTML unusedQuery unusedInput unusedOutput anyMonad + . MonadEffect anyMonad + => H.Component HH.HTML unusedQuery unusedInput unusedOutput anyMonad containerComponent = Hooks.component \rec _ -> Hooks.do + parentRenders <- useRenderCount state /\ stateIdx <- Hooks.useState { a: Nothing, b: Nothing, c: Nothing } let _a = SProxy :: SProxy "a" @@ -65,6 +70,8 @@ containerComponent = Hooks.component \rec _ -> Hooks.do Hooks.put stateIdx { a, b, c } ] [ HH.text "Check states now" ] + , HH.p_ + [ HH.text ("Parent has been rendered " <> show parentRenders <> " time(s)") ] ] data QueryA a = IsOn (Boolean -> a) @@ -128,3 +135,14 @@ componentC = Hooks.component \rec _ -> Hooks.do , HE.onValueInput (Just <<< Hooks.put stateIdx) ] ] + +useRenderCount + :: forall m a + . MonadEffect m + => Hooked m a (UseEffect (UseRef Int a)) Int +useRenderCount = Hooks.do + renders /\ rendersRef <- Hooks.useRef 1 + Hooks.captures {} Hooks.useTickEffect do + liftEffect (Ref.modify_ (_ + 1) rendersRef) + mempty + Hooks.pure renders \ No newline at end of file From 85190a0386b45fe07291bb76184e47395bfb7c7d Mon Sep 17 00:00:00 2001 From: Peter Murphy <26548438+ptrfrncsmrph@users.noreply.github.com> Date: Sat, 16 Jan 2021 22:15:51 -0500 Subject: [PATCH 11/11] s/re-rendered/rendered --- recipes/ComponentsInputReactHooks/README.md | 2 +- recipes/ComponentsReactHooks/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/recipes/ComponentsInputReactHooks/README.md b/recipes/ComponentsInputReactHooks/README.md index 49ca3a6b..7e145003 100644 --- a/recipes/ComponentsInputReactHooks/README.md +++ b/recipes/ComponentsInputReactHooks/README.md @@ -7,7 +7,7 @@ Each time the parent's state updates, it will pass a new prop value into the chi ### Browser 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. -The parent will also display how many times it has been re-rendered. +The parent will also display how many times it has been rendered. ## Dependencies Used: diff --git a/recipes/ComponentsReactHooks/README.md b/recipes/ComponentsReactHooks/README.md index 45c22c46..a1af9de8 100644 --- a/recipes/ComponentsReactHooks/README.md +++ b/recipes/ComponentsReactHooks/README.md @@ -7,7 +7,7 @@ Demonstrates how to nest one React Hooks-based component inside another and send ### Browser 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 button state. -The parent will also display how many times it has been re-rendered. +The parent will also display how many times it has been rendered. ## Dependencies Used: