chore: continue to consolidate
This commit is contained in:
1 parent
fa106be1f5
commit
21e00c114e
5 files changed
+241
-237
No files matched your search
+26
-18
@@ -7,16 +7,12 @@ To build new apps with Dioxus or to extend the ecosystem with new hooks or compo
|
|||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn app(cx: Scope<()>) -> Element {
|
fn app(cx: Scope<()>) -> Element {
|
||||||
cx.render(rsx!(
|
rsx!(cx, div { "hello world" })
|
||||||
div { "hello world" }
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut renderer = SomeRenderer::new();
|
let mut renderer = SomeRenderer::new();
|
||||||
|
|
||||||
|
|
||||||
// Creating a new virtualdom from a component
|
// Creating a new virtualdom from a component
|
||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
|
|
||||||
@@ -25,23 +21,35 @@ fn main() {
|
|||||||
renderer.apply(edits);
|
renderer.apply(edits);
|
||||||
|
|
||||||
// Injecting events
|
// Injecting events
|
||||||
dom.handle_message(SchedulerMsg::UserEvent());
|
dom.handle_message(SchedulerMsg::Event(UserEvent {
|
||||||
|
scope_id: None,
|
||||||
|
priority: EventPriority::High,
|
||||||
|
element: ElementId(0),
|
||||||
|
name: "onclick",
|
||||||
|
data: Arc::new(()),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// polling asynchronously
|
||||||
|
dom.wait_for_work().await;
|
||||||
|
|
||||||
|
// working with a deadline
|
||||||
|
if let Some(edits) = dom.work_with_deadline(|| false) {
|
||||||
|
renderer.apply(edits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// getting state of scopes
|
||||||
|
let scope = dom.get_scope(ScopeId(0)).unwrap();
|
||||||
|
|
||||||
|
// iterating through the tree
|
||||||
|
match scope.root_node() {
|
||||||
|
VNodes::Text(vtext) => dbg!(vtext),
|
||||||
|
VNodes::Element(vel) => dbg!(vel),
|
||||||
|
_ => todo!()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
## Internals
|
## Internals
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_core as dioxus;
|
||||||
|
use dioxus_core_macro::*;
|
||||||
|
use dioxus_html as dioxus_elements;
|
||||||
|
|
||||||
|
// very tiny hello world
|
||||||
|
fn main() {
|
||||||
|
dioxus::VirtualDom::new(|cx| rsx!(cx, "hello world"));
|
||||||
|
}
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
//! This file handles the supporting infrastructure for the `Component` trait and `Properties` which makes it possible
|
|
||||||
//! for components to be used within Nodes.
|
|
||||||
//!
|
|
||||||
//! Note - using the builder pattern does not required the Properties trait to be implemented - the only thing that matters is
|
|
||||||
//! if the type supports PartialEq. The Properties trait is used by the rsx! and html! macros to generate the type-safe builder
|
|
||||||
//! that ensures compile-time required and optional fields on cx.
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
innerlude::{Element, Scope},
|
|
||||||
LazyNodes,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct FragmentProps<'a>(Element<'a>);
|
|
||||||
pub struct FragmentBuilder<'a, const BUILT: bool>(Element<'a>);
|
|
||||||
impl<'a> FragmentBuilder<'a, false> {
|
|
||||||
pub fn children(self, children: Element<'a>) -> FragmentBuilder<'a, true> {
|
|
||||||
FragmentBuilder(children)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a, const A: bool> FragmentBuilder<'a, A> {
|
|
||||||
pub fn build(self) -> FragmentProps<'a> {
|
|
||||||
FragmentProps(self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Access the children elements passed into the component
|
|
||||||
///
|
|
||||||
/// This enables patterns where a component is passed children from its parent.
|
|
||||||
///
|
|
||||||
/// ## Details
|
|
||||||
///
|
|
||||||
/// Unlike React, Dioxus allows *only* lists of children to be passed from parent to child - not arbitrary functions
|
|
||||||
/// or classes. If you want to generate nodes instead of accepting them as a list, consider declaring a closure
|
|
||||||
/// on the props that takes Context.
|
|
||||||
///
|
|
||||||
/// If a parent passes children into a component, the child will always re-render when the parent re-renders. In other
|
|
||||||
/// words, a component cannot be automatically memoized if it borrows nodes from its parent, even if the component's
|
|
||||||
/// props are valid for the static lifetime.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
///
|
|
||||||
/// ```rust, ignore
|
|
||||||
/// fn App(cx: Scope<()>) -> Element {
|
|
||||||
/// cx.render(rsx!{
|
|
||||||
/// CustomCard {
|
|
||||||
/// h1 {}2
|
|
||||||
/// p {}
|
|
||||||
/// }
|
|
||||||
/// })
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// #[derive(PartialEq, Props)]
|
|
||||||
/// struct CardProps {
|
|
||||||
/// children: Element
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn CustomCard(cx: Scope<CardProps>) -> Element {
|
|
||||||
/// cx.render(rsx!{
|
|
||||||
/// div {
|
|
||||||
/// h1 {"Title card"}
|
|
||||||
/// {cx.props.children}
|
|
||||||
/// }
|
|
||||||
/// })
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
impl<'a> Properties for FragmentProps<'a> {
|
|
||||||
type Builder = FragmentBuilder<'a, false>;
|
|
||||||
const IS_STATIC: bool = false;
|
|
||||||
fn builder() -> Self::Builder {
|
|
||||||
FragmentBuilder(None)
|
|
||||||
}
|
|
||||||
unsafe fn memoize(&self, _other: &Self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create inline fragments using Component syntax.
|
|
||||||
///
|
|
||||||
/// ## Details
|
|
||||||
///
|
|
||||||
/// Fragments capture a series of children without rendering extra nodes.
|
|
||||||
///
|
|
||||||
/// Creating fragments explicitly with the Fragment component is particularly useful when rendering lists or tables and
|
|
||||||
/// a key is needed to identify each item.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
///
|
|
||||||
/// ```rust, ignore
|
|
||||||
/// rsx!{
|
|
||||||
/// Fragment { key: "abc" }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ## Usage
|
|
||||||
///
|
|
||||||
/// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
|
|
||||||
/// Try to avoid highly nested fragments if you can. Unlike React, there is no protection against infinitely nested fragments.
|
|
||||||
///
|
|
||||||
/// This function defines a dedicated `Fragment` component that can be used to create inline fragments in the RSX macro.
|
|
||||||
///
|
|
||||||
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
|
|
||||||
#[allow(non_upper_case_globals, non_snake_case)]
|
|
||||||
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
|
|
||||||
let i = cx.props.0.as_ref().map(|f| f.decouple());
|
|
||||||
cx.render(Some(LazyNodes::new(|f| f.fragment_from_iter(i))))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
|
|
||||||
/// on how to memoize the props and some additional optimizations that can be made. We strongly encourage using the
|
|
||||||
/// derive macro to implement the `Properties` trait automatically as guarantee that your memoization strategy is safe.
|
|
||||||
///
|
|
||||||
/// If your props are 'static, then Dioxus will require that they also be PartialEq for the derived memoize strategy. However,
|
|
||||||
/// if your props borrow data, then the memoization strategy will simply default to "false" and the PartialEq will be ignored.
|
|
||||||
/// This tends to be useful when props borrow something that simply cannot be compared (IE a reference to a closure);
|
|
||||||
///
|
|
||||||
/// By default, the memoization strategy is very conservative, but can be tuned to be more aggressive manually. However,
|
|
||||||
/// this is only safe if the props are 'static - otherwise you might borrow references after-free.
|
|
||||||
///
|
|
||||||
/// We strongly suggest that any changes to memoization be done at the "PartialEq" level for 'static props. Additionally,
|
|
||||||
/// we advise the use of smart pointers in cases where memoization is important.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
///
|
|
||||||
/// For props that are 'static:
|
|
||||||
/// ```rust, ignore ignore
|
|
||||||
/// #[derive(Props, PartialEq)]
|
|
||||||
/// struct MyProps {
|
|
||||||
/// data: String
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// For props that borrow:
|
|
||||||
///
|
|
||||||
/// ```rust, ignore ignore
|
|
||||||
/// #[derive(Props)]
|
|
||||||
/// struct MyProps<'a >{
|
|
||||||
/// data: &'a str
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub trait Properties: Sized {
|
|
||||||
type Builder;
|
|
||||||
const IS_STATIC: bool;
|
|
||||||
fn builder() -> Self::Builder;
|
|
||||||
|
|
||||||
/// Memoization can only happen if the props are valid for the 'static lifetime
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// The user must know if their props are static, but if they make a mistake, UB happens
|
|
||||||
/// Therefore it's unsafe to memoize.
|
|
||||||
unsafe fn memoize(&self, other: &Self) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Properties for () {
|
|
||||||
type Builder = EmptyBuilder;
|
|
||||||
const IS_STATIC: bool = true;
|
|
||||||
fn builder() -> Self::Builder {
|
|
||||||
EmptyBuilder {}
|
|
||||||
}
|
|
||||||
unsafe fn memoize(&self, _other: &Self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// We allow components to use the () generic parameter if they have no props. This impl enables the "build" method
|
|
||||||
// that the macros use to anonymously complete prop construction.
|
|
||||||
pub struct EmptyBuilder;
|
|
||||||
impl EmptyBuilder {
|
|
||||||
pub fn build(self) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
|
|
||||||
/// to initialize a component's props.
|
|
||||||
pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element) -> T::Builder {
|
|
||||||
T::builder()
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
pub(crate) mod component;
|
|
||||||
pub(crate) mod diff;
|
pub(crate) mod diff;
|
||||||
pub(crate) mod lazynodes;
|
pub(crate) mod lazynodes;
|
||||||
pub(crate) mod mutations;
|
pub(crate) mod mutations;
|
||||||
@@ -10,7 +9,6 @@ pub(crate) mod scopes;
|
|||||||
pub(crate) mod virtual_dom;
|
pub(crate) mod virtual_dom;
|
||||||
|
|
||||||
pub(crate) mod innerlude {
|
pub(crate) mod innerlude {
|
||||||
pub use crate::component::*;
|
|
||||||
pub(crate) use crate::diff::*;
|
pub(crate) use crate::diff::*;
|
||||||
pub use crate::lazynodes::*;
|
pub use crate::lazynodes::*;
|
||||||
pub use crate::mutations::*;
|
pub use crate::mutations::*;
|
||||||
@@ -69,12 +67,12 @@ pub use crate::innerlude::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::component::{fc_to_builder, Fragment, Properties};
|
|
||||||
pub use crate::innerlude::Scope;
|
pub use crate::innerlude::Scope;
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
Component, DioxusElement, Element, EventHandler, LazyNodes, NodeFactory, ScopeState,
|
Component, DioxusElement, Element, EventHandler, LazyNodes, NodeFactory, ScopeState,
|
||||||
};
|
};
|
||||||
pub use crate::nodes::VNode;
|
pub use crate::nodes::VNode;
|
||||||
|
pub use crate::virtual_dom::{fc_to_builder, Fragment, Properties};
|
||||||
pub use crate::VirtualDom;
|
pub use crate::VirtualDom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -697,6 +697,51 @@ impl Drop for VirtualDom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PollTasks<'a>(&'a mut ScopeArena);
|
||||||
|
|
||||||
|
impl<'a> Future for PollTasks<'a> {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let mut all_pending = true;
|
||||||
|
|
||||||
|
let mut unfinished_tasks: SmallVec<[_; 10]> = smallvec::smallvec![];
|
||||||
|
let mut scopes_to_clear: SmallVec<[_; 10]> = smallvec::smallvec![];
|
||||||
|
|
||||||
|
// Poll every scope manually
|
||||||
|
for fut in self.0.pending_futures.borrow().iter().copied() {
|
||||||
|
let scope = self.0.get_scope(fut).expect("Scope should never be moved");
|
||||||
|
|
||||||
|
let mut items = scope.items.borrow_mut();
|
||||||
|
|
||||||
|
// really this should just be retain_mut but that doesn't exist yet
|
||||||
|
while let Some(mut task) = items.tasks.pop() {
|
||||||
|
if task.as_mut().poll(cx).is_ready() {
|
||||||
|
all_pending = false
|
||||||
|
} else {
|
||||||
|
unfinished_tasks.push(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if unfinished_tasks.is_empty() {
|
||||||
|
scopes_to_clear.push(fut);
|
||||||
|
}
|
||||||
|
|
||||||
|
items.tasks.extend(unfinished_tasks.drain(..));
|
||||||
|
}
|
||||||
|
|
||||||
|
for scope in scopes_to_clear {
|
||||||
|
self.0.pending_futures.borrow_mut().remove(&scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the future if any singular task is ready
|
||||||
|
match all_pending {
|
||||||
|
true => Poll::Pending,
|
||||||
|
false => Poll::Ready(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SchedulerMsg {
|
pub enum SchedulerMsg {
|
||||||
// events from the host
|
// events from the host
|
||||||
@@ -810,47 +855,165 @@ pub enum EventPriority {
|
|||||||
Low = 0,
|
Low = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PollTasks<'a>(&'a mut ScopeArena);
|
pub struct FragmentProps<'a>(Element<'a>);
|
||||||
|
pub struct FragmentBuilder<'a, const BUILT: bool>(Element<'a>);
|
||||||
impl<'a> Future for PollTasks<'a> {
|
impl<'a> FragmentBuilder<'a, false> {
|
||||||
type Output = ();
|
pub fn children(self, children: Element<'a>) -> FragmentBuilder<'a, true> {
|
||||||
|
FragmentBuilder(children)
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let mut all_pending = true;
|
|
||||||
|
|
||||||
let mut unfinished_tasks: SmallVec<[_; 10]> = smallvec::smallvec![];
|
|
||||||
let mut scopes_to_clear: SmallVec<[_; 10]> = smallvec::smallvec![];
|
|
||||||
|
|
||||||
// Poll every scope manually
|
|
||||||
for fut in self.0.pending_futures.borrow().iter().copied() {
|
|
||||||
let scope = self.0.get_scope(fut).expect("Scope should never be moved");
|
|
||||||
|
|
||||||
let mut items = scope.items.borrow_mut();
|
|
||||||
|
|
||||||
// really this should just be retain_mut but that doesn't exist yet
|
|
||||||
while let Some(mut task) = items.tasks.pop() {
|
|
||||||
if task.as_mut().poll(cx).is_ready() {
|
|
||||||
all_pending = false
|
|
||||||
} else {
|
|
||||||
unfinished_tasks.push(task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if unfinished_tasks.is_empty() {
|
|
||||||
scopes_to_clear.push(fut);
|
|
||||||
}
|
|
||||||
|
|
||||||
items.tasks.extend(unfinished_tasks.drain(..));
|
|
||||||
}
|
|
||||||
|
|
||||||
for scope in scopes_to_clear {
|
|
||||||
self.0.pending_futures.borrow_mut().remove(&scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve the future if any singular task is ready
|
|
||||||
match all_pending {
|
|
||||||
true => Poll::Pending,
|
|
||||||
false => Poll::Ready(()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<'a, const A: bool> FragmentBuilder<'a, A> {
|
||||||
|
pub fn build(self) -> FragmentProps<'a> {
|
||||||
|
FragmentProps(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access the children elements passed into the component
|
||||||
|
///
|
||||||
|
/// This enables patterns where a component is passed children from its parent.
|
||||||
|
///
|
||||||
|
/// ## Details
|
||||||
|
///
|
||||||
|
/// Unlike React, Dioxus allows *only* lists of children to be passed from parent to child - not arbitrary functions
|
||||||
|
/// or classes. If you want to generate nodes instead of accepting them as a list, consider declaring a closure
|
||||||
|
/// on the props that takes Context.
|
||||||
|
///
|
||||||
|
/// If a parent passes children into a component, the child will always re-render when the parent re-renders. In other
|
||||||
|
/// words, a component cannot be automatically memoized if it borrows nodes from its parent, even if the component's
|
||||||
|
/// props are valid for the static lifetime.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust, ignore
|
||||||
|
/// fn App(cx: Scope<()>) -> Element {
|
||||||
|
/// cx.render(rsx!{
|
||||||
|
/// CustomCard {
|
||||||
|
/// h1 {}2
|
||||||
|
/// p {}
|
||||||
|
/// }
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[derive(PartialEq, Props)]
|
||||||
|
/// struct CardProps {
|
||||||
|
/// children: Element
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn CustomCard(cx: Scope<CardProps>) -> Element {
|
||||||
|
/// cx.render(rsx!{
|
||||||
|
/// div {
|
||||||
|
/// h1 {"Title card"}
|
||||||
|
/// {cx.props.children}
|
||||||
|
/// }
|
||||||
|
/// })
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
impl<'a> Properties for FragmentProps<'a> {
|
||||||
|
type Builder = FragmentBuilder<'a, false>;
|
||||||
|
const IS_STATIC: bool = false;
|
||||||
|
fn builder() -> Self::Builder {
|
||||||
|
FragmentBuilder(None)
|
||||||
|
}
|
||||||
|
unsafe fn memoize(&self, _other: &Self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create inline fragments using Component syntax.
|
||||||
|
///
|
||||||
|
/// ## Details
|
||||||
|
///
|
||||||
|
/// Fragments capture a series of children without rendering extra nodes.
|
||||||
|
///
|
||||||
|
/// Creating fragments explicitly with the Fragment component is particularly useful when rendering lists or tables and
|
||||||
|
/// a key is needed to identify each item.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust, ignore
|
||||||
|
/// rsx!{
|
||||||
|
/// Fragment { key: "abc" }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ## Usage
|
||||||
|
///
|
||||||
|
/// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
|
||||||
|
/// Try to avoid highly nested fragments if you can. Unlike React, there is no protection against infinitely nested fragments.
|
||||||
|
///
|
||||||
|
/// This function defines a dedicated `Fragment` component that can be used to create inline fragments in the RSX macro.
|
||||||
|
///
|
||||||
|
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
|
||||||
|
#[allow(non_upper_case_globals, non_snake_case)]
|
||||||
|
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
|
||||||
|
let i = cx.props.0.as_ref().map(|f| f.decouple());
|
||||||
|
cx.render(Some(LazyNodes::new(|f| f.fragment_from_iter(i))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
|
||||||
|
/// on how to memoize the props and some additional optimizations that can be made. We strongly encourage using the
|
||||||
|
/// derive macro to implement the `Properties` trait automatically as guarantee that your memoization strategy is safe.
|
||||||
|
///
|
||||||
|
/// If your props are 'static, then Dioxus will require that they also be PartialEq for the derived memoize strategy. However,
|
||||||
|
/// if your props borrow data, then the memoization strategy will simply default to "false" and the PartialEq will be ignored.
|
||||||
|
/// This tends to be useful when props borrow something that simply cannot be compared (IE a reference to a closure);
|
||||||
|
///
|
||||||
|
/// By default, the memoization strategy is very conservative, but can be tuned to be more aggressive manually. However,
|
||||||
|
/// this is only safe if the props are 'static - otherwise you might borrow references after-free.
|
||||||
|
///
|
||||||
|
/// We strongly suggest that any changes to memoization be done at the "PartialEq" level for 'static props. Additionally,
|
||||||
|
/// we advise the use of smart pointers in cases where memoization is important.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// For props that are 'static:
|
||||||
|
/// ```rust, ignore ignore
|
||||||
|
/// #[derive(Props, PartialEq)]
|
||||||
|
/// struct MyProps {
|
||||||
|
/// data: String
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// For props that borrow:
|
||||||
|
///
|
||||||
|
/// ```rust, ignore ignore
|
||||||
|
/// #[derive(Props)]
|
||||||
|
/// struct MyProps<'a >{
|
||||||
|
/// data: &'a str
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub trait Properties: Sized {
|
||||||
|
type Builder;
|
||||||
|
const IS_STATIC: bool;
|
||||||
|
fn builder() -> Self::Builder;
|
||||||
|
|
||||||
|
/// Memoization can only happen if the props are valid for the 'static lifetime
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// The user must know if their props are static, but if they make a mistake, UB happens
|
||||||
|
/// Therefore it's unsafe to memoize.
|
||||||
|
unsafe fn memoize(&self, other: &Self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Properties for () {
|
||||||
|
type Builder = EmptyBuilder;
|
||||||
|
const IS_STATIC: bool = true;
|
||||||
|
fn builder() -> Self::Builder {
|
||||||
|
EmptyBuilder {}
|
||||||
|
}
|
||||||
|
unsafe fn memoize(&self, _other: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We allow components to use the () generic parameter if they have no props. This impl enables the "build" method
|
||||||
|
// that the macros use to anonymously complete prop construction.
|
||||||
|
pub struct EmptyBuilder;
|
||||||
|
impl EmptyBuilder {
|
||||||
|
pub fn build(self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
|
||||||
|
/// to initialize a component's props.
|
||||||
|
pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element) -> T::Builder {
|
||||||
|
T::builder()
|
||||||
|
}
|
||||||
Reference in new issue
Block a user