Skip to content

Commit 1570e5d

Browse files
committed
refactor: made basic examples use reactive state
Not the showcase example yet, but that's getting a major overhaul.
1 parent a355157 commit 1570e5d

File tree

10 files changed

+61
-74
lines changed

10 files changed

+61
-74
lines changed

README.md

+5-9
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,15 @@ Here's a taste of Perseus (see [the _tiny_ example](https://github.com/arctic-he
2222

2323
```rust
2424
use perseus::{define_app, ErrorPages, Template};
25-
use sycamore::template;
25+
use sycamore::view;
2626
define_app! {
2727
templates: [
28-
Template::<G>::new("index").template(|_| {
29-
template! {
30-
p { "Hello World!" }
31-
}
28+
Template::<G>::new("index").template(|_| view! {
29+
p { "Hello World!" }
3230
})
3331
],
34-
error_pages: ErrorPages::new(|url, status, err, _| {
35-
template! {
36-
p { (format!("An error with HTTP code {} occurred at '{}': '{}'.", status, url, err)) }
37-
}
32+
error_pages: ErrorPages::new(|url, status, err, _| view! {
33+
p { (format!("An error with HTTP code {} occurred at '{}': '{}'.", status, url, err)) }
3834
})
3935
}
4036
```

examples/basic/src/templates/about.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use perseus::Template;
2-
use sycamore::prelude::{component, view, Html, SsrNode, View};
2+
use sycamore::prelude::{view, Html, SsrNode, View};
33

4-
#[perseus::template(AboutPage)]
5-
#[component(AboutPage<G>)]
4+
#[perseus::template_rx(AboutPage)]
65
pub fn about_page() -> View<G> {
76
view! {
87
p { "About." }

examples/basic/src/templates/index.rs

+12-14
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,48 @@ use perseus::{
22
http::header::{HeaderMap, HeaderName},
33
Html, RenderFnResultWithCause, SsrNode, Template,
44
};
5-
use serde::{Deserialize, Serialize};
6-
use sycamore::prelude::{component, view, View};
5+
use sycamore::prelude::{view, View};
76

8-
#[derive(Serialize, Deserialize, Debug)]
9-
pub struct IndexPageProps {
7+
#[perseus::make_rx(IndexPageStateRx)]
8+
pub struct IndexPageState {
109
pub greeting: String,
1110
}
1211

13-
#[perseus::template(IndexPage)]
14-
#[component(IndexPage<G>)]
15-
pub fn index_page(props: IndexPageProps) -> View<G> {
12+
#[perseus::template_rx(IndexPage)]
13+
pub fn index_page(state: IndexPageStateRx) -> View<G> {
1614
view! {
17-
p {(props.greeting)}
15+
p { (state.greeting.get()) }
1816
a(href = "about", id = "about-link") { "About!" }
1917
}
2018
}
2119

2220
pub fn get_template<G: Html>() -> Template<G> {
2321
Template::new("index")
24-
.build_state_fn(get_build_props)
22+
.build_state_fn(get_build_state)
2523
.template(index_page)
2624
.head(head)
2725
.set_headers_fn(set_headers)
2826
}
2927

3028
#[perseus::head]
31-
pub fn head(_props: IndexPageProps) -> View<SsrNode> {
29+
pub fn head(_props: IndexPageState) -> View<SsrNode> {
3230
view! {
3331
title { "Index Page | Perseus Example – Basic" }
3432
}
3533
}
3634

3735
#[perseus::autoserde(build_state)]
38-
pub async fn get_build_props(
36+
pub async fn get_build_state(
3937
_path: String,
4038
_locale: String,
41-
) -> RenderFnResultWithCause<IndexPageProps> {
42-
Ok(IndexPageProps {
39+
) -> RenderFnResultWithCause<IndexPageState> {
40+
Ok(IndexPageState {
4341
greeting: "Hello World!".to_string(),
4442
})
4543
}
4644

4745
#[perseus::autoserde(set_headers)]
48-
pub fn set_headers(props: Option<IndexPageProps>) -> HeaderMap {
46+
pub fn set_headers(props: Option<IndexPageState>) -> HeaderMap {
4947
let mut map = HeaderMap::new();
5048
map.insert(
5149
HeaderName::from_lowercase(b"x-greeting").unwrap(),

examples/fetching/src/index.rs

+21-17
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
use perseus::{Html, RenderFnResultWithCause, Template};
2-
use serde::{Deserialize, Serialize};
32
use sycamore::prelude::*;
43

5-
#[derive(Serialize, Deserialize)]
6-
pub struct IndexProps {
7-
ip: String,
4+
#[perseus::make_rx(IndexPageStateRx)]
5+
pub struct IndexPageState {
6+
server_ip: String,
7+
browser_ip: String,
88
}
99

10-
#[perseus::template(IndexPage)]
11-
#[component(IndexPage<G>)]
12-
pub fn index_page(IndexProps { ip }: IndexProps) -> View<G> {
13-
// This will store the message that we get
14-
// Until we've got it, we'll display `fetching...`
15-
let message = Signal::new("fetching...".to_string());
16-
10+
#[perseus::template_rx(IndexPage)]
11+
pub fn index_page(
12+
IndexPageStateRx {
13+
server_ip,
14+
browser_ip,
15+
}: IndexPageStateRx,
16+
) -> View<G> {
1717
// This will only run in the browser
1818
// `reqwasm` wraps browser-specific APIs, so we don't want it running on the server
1919
if G::IS_BROWSER {
2020
// Spawn a `Future` on this thread to fetch the data (`spawn_local` is re-exported from `wasm-bindgen-futures`)
2121
// Don't worry, this doesn't need to be sent to JavaScript for execution
2222
//
2323
// We want to access the `message` `Signal`, so we'll clone it in (and then we need `move` because this has to be `'static`)
24-
perseus::spawn_local(cloned!(message => async move {
24+
perseus::spawn_local(cloned!(browser_ip => async move {
2525
// This interface may seem weird, that's because it wraps the browser's Fetch API
2626
// We request from a local path here because of CORS restrictions (see the book)
2727
let body = reqwasm::http::Request::get("/message")
@@ -31,13 +31,13 @@ pub fn index_page(IndexProps { ip }: IndexProps) -> View<G> {
3131
.text()
3232
.await
3333
.unwrap();
34-
message.set(body);
34+
browser_ip.set(body);
3535
}));
3636
}
3737

3838
view! {
39-
p { (format!("IP address of the builder was: {}", ip)) }
40-
p { (format!("The message at `/message` is: {}", message.get())) }
39+
p { (format!("IP address of the server was: {}", server_ip.get())) }
40+
p { (format!("The message at `/message` is: {}", browser_ip.get())) }
4141
}
4242
}
4343

@@ -51,7 +51,7 @@ pub fn get_template<G: Html>() -> Template<G> {
5151
pub async fn get_build_state(
5252
_path: String,
5353
_locale: String,
54-
) -> RenderFnResultWithCause<IndexProps> {
54+
) -> RenderFnResultWithCause<IndexPageState> {
5555
// We'll cache the result with `try_cache_res`, which means we only make the request once, and future builds will use the cached result (speeds up development)
5656
let body = perseus::cache_fallible_res(
5757
"ipify",
@@ -63,5 +63,9 @@ pub async fn get_build_state(
6363
false,
6464
)
6565
.await?;
66-
Ok(IndexProps { ip: body })
66+
// We'll start with a placeholder for the browser's IP, which will be fetched on the client-side
67+
Ok(IndexPageState {
68+
server_ip: body,
69+
browser_ip: "fetching...".to_string(),
70+
})
6771
}

examples/i18n/src/templates/about.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use perseus::{t, Template};
2-
use sycamore::prelude::{component, view, Html, View};
2+
use sycamore::prelude::{view, Html, View};
33

4-
#[perseus::template(AboutPage)]
5-
#[component(AboutPage<G>)]
4+
#[perseus::template_rx(AboutPage)]
65
pub fn about_page() -> View<G> {
76
view! {
87
p { (t!("about")) }

examples/i18n/src/templates/index.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use perseus::{link, t, Template};
2-
use sycamore::prelude::{component, view, Html, View};
2+
use sycamore::prelude::{view, Html, View};
33

4-
#[perseus::template(IndexPage)]
5-
#[component(IndexPage<G>)]
4+
#[perseus::template_rx(IndexPage)]
65
pub fn index_page() -> View<G> {
76
let username = "User";
87
view! {

examples/i18n/src/templates/post.rs

+9-11
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,22 @@
11
use perseus::{link, RenderFnResult, RenderFnResultWithCause, Template};
2-
use serde::{Deserialize, Serialize};
3-
use sycamore::prelude::{component, view, Html, View};
2+
use sycamore::prelude::{view, Html, View};
43

5-
#[derive(Serialize, Deserialize)]
6-
pub struct PostPageProps {
4+
#[perseus::make_rx(PostPageStateRx)]
5+
pub struct PostPageState {
76
title: String,
87
content: String,
98
}
109

11-
#[perseus::template(PostPage)]
12-
#[component(PostPage<G>)]
13-
pub fn post_page(props: PostPageProps) -> View<G> {
10+
#[perseus::template_rx(PostPage)]
11+
pub fn post_page(props: PostPageStateRx) -> View<G> {
1412
let title = props.title;
1513
let content = props.content;
1614
view! {
1715
h1 {
18-
(title)
16+
(title.get())
1917
}
2018
p {
21-
(content)
19+
(content.get())
2220
}
2321
a(href = link!("/post")) { "Root post page" }
2422
br()
@@ -37,15 +35,15 @@ pub fn get_template<G: Html>() -> Template<G> {
3735
pub async fn get_static_props(
3836
path: String,
3937
_locale: String,
40-
) -> RenderFnResultWithCause<PostPageProps> {
38+
) -> RenderFnResultWithCause<PostPageState> {
4139
// This is just an example
4240
let title = urlencoding::decode(&path).unwrap();
4341
let content = format!(
4442
"This is a post entitled '{}'. Its original slug was '{}'.",
4543
title, path
4644
);
4745

48-
Ok(PostPageProps {
46+
Ok(PostPageState {
4947
title: title.to_string(),
5048
content,
5149
}) // This `?` declares the default, that the server is the cause of the error

examples/plugins/src/templates/about.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
use perseus::Template;
2-
use sycamore::prelude::{component, view, Html, SsrNode, View};
2+
use sycamore::prelude::{view, Html, SsrNode, View};
33

44
// This page will actually be replaced entirely by a plugin!
5-
#[perseus::template(AboutPage)]
6-
#[component(AboutPage<G>)]
5+
#[perseus::template_rx(AboutPage)]
76
pub fn about_page() -> View<G> {
87
view! {
98
p { "About." }

examples/plugins/src/templates/index.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use perseus::{Html, Template};
2-
use sycamore::prelude::{component, view, SsrNode, View};
2+
use sycamore::prelude::{view, SsrNode, View};
33

4-
#[perseus::template(IndexPage)]
5-
#[component(IndexPage<G>)]
4+
#[perseus::template_rx(IndexPage)]
65
pub fn index_page() -> View<G> {
76
view! {
87
p { "Hello World!" }

examples/tiny/src/lib.rs

+4-8
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,11 @@ use perseus::{define_app, ErrorPages, Template};
22
use sycamore::view;
33
define_app! {
44
templates: [
5-
Template::<G>::new("index").template(|_| {
6-
view! {
7-
p { "Hello World!" }
8-
}
5+
Template::<G>::new("index").template(|_| view! {
6+
p { "Hello World!" }
97
})
108
],
11-
error_pages: ErrorPages::new(|url, status, err, _| {
12-
view! {
13-
p { (format!("An error with HTTP code {} occurred at '{}': '{}'.", status, url, err)) }
14-
}
9+
error_pages: ErrorPages::new(|url, status, err, _| view! {
10+
p { (format!("An error with HTTP code {} occurred at '{}': '{}'.", status, url, err)) }
1511
})
1612
}

0 commit comments

Comments
 (0)