react_rs_elements/
suspense.rs1use crate::node::{IntoNode, Node};
2use react_rs_core::resource::{Resource, ResourceState};
3use std::rc::Rc;
4
5pub struct SuspenseData {
6 pub fallback: Box<Node>,
7 pub children: Box<Node>,
8 pub loading_signal: Rc<dyn Fn() -> bool>,
9}
10
11pub struct ErrorBoundaryData {
12 pub error_fallback: Rc<dyn Fn(String) -> Node>,
13 pub children: Box<Node>,
14 pub error_signal: Rc<dyn Fn() -> Option<String>>,
15}
16
17pub fn suspense<T: Clone + 'static>(
18 resource: &Resource<T>,
19 fallback: impl IntoNode,
20 children: impl IntoNode,
21) -> Node {
22 let state = resource.state();
23 Node::Suspense(SuspenseData {
24 fallback: Box::new(fallback.into_node()),
25 children: Box::new(children.into_node()),
26 loading_signal: Rc::new(move || state.get().is_loading()),
27 })
28}
29
30pub fn error_boundary<T: Clone + 'static>(
31 resource: &Resource<T>,
32 error_fallback: impl Fn(String) -> Node + 'static,
33 children: impl IntoNode,
34) -> Node {
35 let state = resource.state();
36 Node::ErrorBoundary(ErrorBoundaryData {
37 error_fallback: Rc::new(error_fallback),
38 children: Box::new(children.into_node()),
39 error_signal: Rc::new(move || match state.get() {
40 ResourceState::Error(e) => Some(e),
41 _ => None,
42 }),
43 })
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49 use crate::html;
50 use react_rs_core::resource::create_resource;
51
52 #[test]
53 fn test_suspense_creates_node() {
54 let resource = create_resource::<String>();
55 let node = suspense(
56 &resource,
57 html::p().text("Loading..."),
58 html::div().text("Content"),
59 );
60 assert!(matches!(node, Node::Suspense(_)));
61 }
62
63 #[test]
64 fn test_error_boundary_creates_node() {
65 let resource = create_resource::<String>();
66 let node = error_boundary(
67 &resource,
68 |err| html::p().text(format!("Error: {}", err)).into_node(),
69 html::div().text("Content"),
70 );
71 assert!(matches!(node, Node::ErrorBoundary(_)));
72 }
73}