Skip to content

Commit 8beb5ee

Browse files
authored
Add RoutingHashReactHooks (#271)
1 parent 6479ff1 commit 8beb5ee

File tree

7 files changed

+195
-0
lines changed

7 files changed

+195
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ Running a web-compatible recipe:
146146
| :heavy_check_mark: | | [ReadPrintFileContentsNode](recipes/ReadPrintFileContentsNode) | Reads a file's contents and prints it to the console. |
147147
| | :heavy_check_mark: | [RoutingHashHalogenClassic](recipes/RoutingHashHalogenClassic) | This recipe shows how to use `purescript-routing` to do client-side hash-based routing in a Halogen-based single-page application (SPA). |
148148
| | :heavy_check_mark: | [RoutingHashLog](recipes/RoutingHashLog) | This recipe demonstrates hash-based routing with `purescript-routing`. No web framework is used. |
149+
| | :heavy_check_mark: ([try](https://try.ps.ai/?github=JordanMartinez/purescript-cookbook/master/recipes/RoutingHashReactHooks/src/Main.purs)) | [RoutingHashReactHooks](recipes/RoutingHashReactHooks) | This recipe shows how to use `purescript-routing` to do client-side hash-based routing in a React-based single-page application (SPA). |
149150
| | :heavy_check_mark: | [RoutingPushHalogenClassic](recipes/RoutingPushHalogenClassic) | This recipe shows how to use `purescript-routing` to do client-side push-state routing in a Halogen-based single-page application (SPA). |
150151
| | :heavy_check_mark: ([try](https://try.ps.ai/?github=JordanMartinez/purescript-cookbook/master/recipes/ShapesHalogenHooks/src/Main.purs)) | [ShapesHalogenHooks](recipes/ShapesHalogenHooks) | Demonstrates rendering of SVG shapes. |
151152
| | :heavy_check_mark: ([try](https://try.ps.ai/?github=JordanMartinez/purescript-cookbook/master/recipes/ShapesReactHooks/src/Main.purs)) | [ShapesReactHooks](recipes/ShapesReactHooks) | Demonstrates rendering of SVG shapes. |
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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# RoutingHashReactHooks
2+
3+
This recipe shows how to use `purescript-routing` to do client-side hash-based routing in a React-based single-page application (SPA).
4+
5+
## Expected Behavior:
6+
7+
### Browser
8+
9+
When you click on the links, you should see:
10+
* The anchor in the URL change
11+
* The current route rendered on the page
12+
* Route changes logged to the dev console
13+
14+
You may also edit the URL directly instead of clicking on the links.
15+
16+
## Dependencies Used:
17+
18+
[react](https://www.npmjs.com/package/react)
19+
[react-dom](https://www.npmjs.com/package/react-dom)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{ name = "RoutingHashReactHooks"
2+
, dependencies =
3+
[ "console"
4+
, "effect"
5+
, "generics-rep"
6+
, "psci-support"
7+
, "react-basic-dom"
8+
, "react-basic-hooks"
9+
, "routing"
10+
]
11+
, packages = ../../packages.dhall
12+
, sources = [ "recipes/RoutingHashReactHooks/src/**/*.purs" ]
13+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
module RoutingHashReactHooks.Main where
2+
3+
import Prelude
4+
import Data.Array as Array
5+
import Data.Foldable as Foldable
6+
import Data.Maybe (Maybe(..))
7+
import Data.String as String
8+
import Effect (Effect)
9+
import Effect.Exception as Exception
10+
import React.Basic (JSX)
11+
import React.Basic as R
12+
import React.Basic.DOM as R
13+
import React.Basic.Hooks (Component, (/\))
14+
import React.Basic.Hooks as React
15+
import Routing.Hash as Hash
16+
import Routing.Match (Match)
17+
import Routing.Match as Match
18+
import Web.HTML as HTML
19+
import Web.HTML.HTMLDocument as HTMLDocument
20+
import Web.HTML.HTMLElement as HTMLElement
21+
import Web.HTML.Location as Location
22+
import Web.HTML.Window as Window
23+
24+
main :: Effect Unit
25+
main = do
26+
maybeBody <- HTMLDocument.body =<< Window.document =<< HTML.window
27+
case maybeBody of
28+
Nothing -> Exception.throw "Could not find body."
29+
Just body -> do
30+
app <- mkApp
31+
R.render (app unit) (HTMLElement.toElement body)
32+
33+
mkApp :: Component Unit
34+
mkApp = do
35+
postIndex <- mkPostIndex
36+
post <- mkPost
37+
postEdit <- mkPostEdit
38+
React.component "App" \_ -> React.do
39+
route /\ setRoute <- React.useState' (Just Home)
40+
React.useEffectOnce do
41+
Hash.matches appRoute \_ newRoute -> setRoute newRoute
42+
React.useEffectOnce do
43+
-- A bit of a hack: this is setting the hash on initial page load, so that
44+
-- it defaults to the `Home` route (by default there is no hash, so no
45+
-- routes match and we'd show "Not found" on first render)
46+
location <- Window.location =<< HTML.window
47+
hash <- Location.hash location
48+
if String.null hash then Location.setHash "/" location else mempty
49+
pure mempty
50+
pure do
51+
R.fragment
52+
[ R.header_ [ headerNav ]
53+
, case route of
54+
Just Home -> R.h1_ [ R.text "Home" ]
55+
Just PostIndex -> postIndex unit
56+
Just (Post postId) -> post postId
57+
Just (PostEdit postId) -> postEdit postId
58+
Nothing -> R.h1_ [ R.text "Not found" ]
59+
]
60+
61+
headerNav :: JSX
62+
headerNav =
63+
R.nav_
64+
[ R.a
65+
{ href: "#/posts"
66+
, children: [ R.text "Posts" ]
67+
}
68+
, R.text " | "
69+
, R.a
70+
{ href: "#/"
71+
, children: [ R.text "Home" ]
72+
}
73+
]
74+
75+
mkPostIndex :: Component Unit
76+
mkPostIndex =
77+
React.component "PostIndex" \_ ->
78+
pure (R.ul_ postLinks)
79+
where
80+
postLinks =
81+
Array.range 1 10
82+
<#> \n ->
83+
R.li_
84+
[ R.a
85+
{ href: "#/posts/" <> show n
86+
, children:
87+
[ R.text ("Post " <> show n) ]
88+
}
89+
]
90+
91+
mkPost :: Component Int
92+
mkPost =
93+
React.component "Post" \n ->
94+
pure do
95+
R.fragment
96+
[ R.h1_ [ R.text ("Post " <> show n) ]
97+
, R.p_
98+
[ R.a
99+
{ href: "#/posts/" <> show n <> "/edit"
100+
, children: [ R.text "Click here" ]
101+
}
102+
, R.text " to edit this post"
103+
]
104+
]
105+
106+
mkPostEdit :: Component Int
107+
mkPostEdit =
108+
React.component "PostEdit" \n ->
109+
pure (R.h1_ [ R.text ("Edit post " <> show n) ])
110+
111+
data AppRoute
112+
= PostIndex
113+
| Post Int
114+
| PostEdit Int
115+
| Home
116+
117+
appRoute :: Match (Maybe AppRoute)
118+
appRoute =
119+
Foldable.oneOf
120+
[ Just <$> postRoute
121+
, Just <$> (Match.root *> pure Home <* Match.end)
122+
, pure Nothing
123+
]
124+
where
125+
postRoute =
126+
Match.root *> Match.lit "posts"
127+
*> Foldable.oneOf
128+
[ PostEdit <$> Match.int <* Match.lit "edit"
129+
, Post <$> Match.int
130+
, pure PostIndex
131+
]
132+
<* Match.end
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<title>RoutingHashReactHooks</title>
7+
</head>
8+
9+
<body>
10+
<script src="./index.js"></script>
11+
</body>
12+
13+
</html>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
"use strict";
2+
require("../../../output/RoutingHashReactHooks.Main/index.js").main();

0 commit comments

Comments
 (0)