Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Components*ReactHooks recipes #249

3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down
1 change: 1 addition & 0 deletions recipes/ComponentsHalogenHooks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
21 changes: 20 additions & 1 deletion recipes/ComponentsHalogenHooks/src/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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 $
Expand All @@ -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
Expand All @@ -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
1 change: 1 addition & 0 deletions recipes/ComponentsInputHalogenHooks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
18 changes: 17 additions & 1 deletion recipes/ComponentsInputHalogenHooks/src/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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_
Expand All @@ -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"
Expand All @@ -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
15 changes: 15 additions & 0 deletions recipes/ComponentsInputReactHooks/.gitignore
Original file line number Diff line number Diff line change
@@ -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/
15 changes: 15 additions & 0 deletions recipes/ComponentsInputReactHooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 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.
The parent will also display how many times it has been rendered.

## Dependencies Used:

[react](https://www.npmjs.com/package/react)
[react-dom](https://www.npmjs.com/package/react-dom)
11 changes: 11 additions & 0 deletions recipes/ComponentsInputReactHooks/spago.dhall
Original file line number Diff line number Diff line change
@@ -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" ]
}
84 changes: 84 additions & 0 deletions recipes/ComponentsInputReactHooks/src/Main.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
module ComponentsInputReactHooks.Main where

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
, Render
, UseEffect
, UseRef
, component
, readRef
, useEffectAlways
, useRef
, useState
, writeRef
, (/\)
)
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
parentRenders <- useRenderCount
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" ]
}
, R.p_
[ R.text ("Parent has been rendered " <> show parentRenders <> " time(s)") ]
]

mkDisplay :: Component Int
mkDisplay =
component "Display" \n ->
pure
$ R.div_
[ 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
12 changes: 12 additions & 0 deletions recipes/ComponentsInputReactHooks/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>ComponentsInputReactHooks</title>
</head>

<body>
<div id="root"></div>
<script src="./index.js"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions recipes/ComponentsInputReactHooks/web/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"use strict";
require("../../../output/ComponentsInputReactHooks.Main/index.js").main();
1 change: 1 addition & 0 deletions recipes/ComponentsMultiTypeHalogenHooks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
20 changes: 19 additions & 1 deletion recipes/ComponentsMultiTypeHalogenHooks/src/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
15 changes: 15 additions & 0 deletions recipes/ComponentsMultiTypeReactHooks/.gitignore
Original file line number Diff line number Diff line change
@@ -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/
15 changes: 15 additions & 0 deletions recipes/ComponentsMultiTypeReactHooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 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.
The parent will also display how many times it has been rendered.

## Dependencies Used:

[react](https://www.npmjs.com/package/react)
[react-dom](https://www.npmjs.com/package/react-dom)
11 changes: 11 additions & 0 deletions recipes/ComponentsMultiTypeReactHooks/spago.dhall
Original file line number Diff line number Diff line change
@@ -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" ]
}
Loading