react/element/
use_render.rs1use std::rc::Rc;
2
3use wasm_bindgen::{closure::Closure, JsValue, UnwrapThrowExt};
4
5use crate::IntoElement;
6
7pub type DynUseRenderFn = dyn Fn() -> Option<crate::Element>;
8
9fn impl_bridge_rust_only_component(js_props: crate::JsProps) -> JsValue {
10 #[cfg(debug_assertions)]
11 let children = js_props.children();
12 #[cfg(debug_assertions)]
13 if children.is_some() {
14 panic!("rust only component should not accept children from js");
15 }
16
17 let bridge = js_props.props_bridge().unwrap();
18
19 let ref_bridge = react_sys::use_ref_usize(bridge);
20
21 let old_bridge = ref_bridge.current();
22 if old_bridge != bridge {
23 let _valid = unsafe { forgotten::try_free_with_usize(old_bridge) };
24
25 #[cfg(debug_assertions)]
26 assert!(_valid, "invalid js props bridge: failed to free");
27
28 ref_bridge.set_current(bridge);
29 }
30
31 let render = unsafe { forgotten::try_get_with_usize::<Box<DynUseRenderFn>>(&bridge) };
32
33 let render = render.expect_throw("invalid js props bridge: failed to get");
34
35 let el = render();
36
37 if let Some(el) = el {
38 el.unsafe_into_js_element().into()
39 } else {
40 JsValue::NULL
41 }
42}
43
44type ClosureBridgeRustOnlyComponent = Closure<dyn Fn(crate::JsProps) -> JsValue>;
45
46fn closure_to_bridge_rust_only_component() -> ClosureBridgeRustOnlyComponent {
47 Closure::wrap(
48 Box::new(impl_bridge_rust_only_component) as Box<dyn Fn(crate::JsProps) -> JsValue>
49 )
50}
51
52pub struct UseRenderElement {
53 pub use_render: Rc<Box<DynUseRenderFn>>,
54 pub key: Option<crate::Key>,
55 pub debug_component_name: Option<JsValue>,
56 pub debug_props: Option<JsValue>,
57}
58
59impl UseRenderElement {
60 #[inline]
61 pub fn wrap_use_render<E: crate::IntoOptionalElement, F: 'static + Fn() -> E>(
62 use_render: F,
63 key: Option<crate::Key>,
64 debug_component_name: Option<JsValue>,
65 debug_props: Option<JsValue>,
66 ) -> Self {
67 let use_render = move || use_render().into_optional_element();
68 Self {
69 use_render: Rc::new(Box::new(use_render)),
70 key,
71 debug_component_name,
72 debug_props,
73 }
74 }
75}
76
77impl std::fmt::Debug for UseRenderElement {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 f.debug_struct("UseRenderElement")
80 .field("use_render", &"Rc<RustClosure>")
81 .field("key", &self.key)
82 .field("debug_component_name", &self.debug_component_name)
83 .field("debug_props", &self.debug_props)
84 .finish()
85 }
86}
87
88impl Clone for UseRenderElement {
89 #[inline]
90 fn clone(&self) -> Self {
91 Self {
92 use_render: self.use_render.clone(),
93 key: self.key.clone(),
94 debug_component_name: self.debug_component_name.clone(),
95 debug_props: self.debug_props.clone(),
96 }
97 }
98}
99
100impl UseRenderElement {
101 #[inline]
107 pub(crate) fn unsafe_create_element_js(self) -> react_sys::Element {
108 let Self {
109 use_render,
110 key,
111 debug_component_name,
112 debug_props,
113 } = self;
114
115 thread_local! {
116 static ADAPTER_FN: ClosureBridgeRustOnlyComponent = closure_to_bridge_rust_only_component();
117 }
118
119 ADAPTER_FN.with(|comp_fn| {
120 use wasm_bindgen::JsCast;
121
122 let obj = js_sys::Object::new();
123 let props: &crate::JsProps = obj.unchecked_ref();
124
125 props.set_key(key.map(Into::into).as_ref());
126
127 #[cfg(debug_assertions)]
128 if let Some(debug_component_name) = debug_component_name {
129 props.set_debug_component_name(&debug_component_name);
130 }
131
132 #[cfg(debug_assertions)]
133 if let Some(debug_props) = debug_props {
134 props.set_debug_props(&debug_props);
135 }
136
137 let k = forgotten::forget_rc(use_render);
138
139 let k = k.into_shared();
140 let k = k.as_usize();
141
142 props.set_props_bridge(Some(*k));
143
144 react_sys::create_element_no_children(comp_fn.as_ref(), props.as_ref())
145 })
146 }
147}
148
149impl crate::IntoElement for UseRenderElement {
150 #[inline]
151 fn into_element(self) -> crate::Element {
152 crate::Element::bridge_use_render_element(self)
153 }
154}
155
156impl Into<crate::Element> for UseRenderElement {
157 #[inline]
158 fn into(self) -> crate::Element {
159 self.into_element()
160 }
161}