Improve inline docs (#2460)

Improve inline docs

* improve incorrect event handler return error message

* Improve event handler docs

* document the eval functions

* document spawn and common spawn errors

* fix event handler docs

* add notes about how you use attributes and elements in rsx

* add doc aliases for attributes and events we rename

* add some more aliases for common search terms

* don't doc ignore any public examples in core

* don't ignore public doc examples in ssr

* don't ignore examples in the dioxus package readme

* add a warning when you launch without a renderer enabled

* fix some outdated element docs

* add a bunch of examples to resource

* add notes about desktop events

* add more docs for use_resource

* add on_unimplemented hint to Dependency

* fix some unresolved links

* add examples to each of the router traits

* add not implemented errors for router traits

* add an example to the routable trait

* expand rsx macro docs

* improve memo docs

* update the dioxus readme

* mention dioxus crate features in the docs

* fix a bunch of doc tests

* fix html doc tests

* fix router doc tests

* fix dioxus signals doc tests

* fix dioxus ssr doc tests

* fix use_future example in the hooks cheat sheet

* add a javascript alias for eval

* fix hook explanation values

* remove unused embed-doc-image dependency
This commit is contained in:
Evan Almloff authored and GitHub committed 2024-06-06 18:15:17 -07:00
1 parent 054351139f
commit 0127501dbf
71 files changed
+4132 -1162

No files matched your search

+20 -9
View File
@@ -450,27 +450,35 @@ impl VNode {
/// This is mostly implemented to help solve the issue where the same component is rendered under two different
/// conditions:
///
/// ```rust, ignore
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # let enabled = true;
/// # #[component]
/// # fn Component(enabled_sign: String) -> Element { todo!() }
/// if enabled {
/// rsx!{ Component { enabled_sign: "abc" } }
/// } else {
/// rsx!{ Component { enabled_sign: "xyz" } }
/// }
/// };
/// ```
///
/// However, we should not that it's explicit in the docs that this is not a guarantee. If you need to preserve state,
/// then you should be passing in separate props instead.
///
/// ```rust, ignore
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # #[component]
/// # fn Component(enabled_sign: String) -> Element { todo!() }
/// # let enabled = true;
/// let props = if enabled {
/// ComponentProps { enabled_sign: "abc" }
/// ComponentProps { enabled_sign: "abc".to_string() }
/// } else {
/// ComponentProps { enabled_sign: "xyz" }
/// ComponentProps { enabled_sign: "xyz".to_string() }
/// };
///
/// rsx! {
/// Component { ..props }
/// }
/// };
/// ```
pub(crate) fn light_diff_templates(
&self,
@@ -702,16 +710,19 @@ impl VNode {
/// Load all of the placeholder nodes for descendents of this root node
///
/// ```rust, ignore
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # let some_text = "hello world";
/// # let some_value = "123";
/// rsx! {
/// div {
/// // This is a placeholder
/// some_value,
/// {some_value}
///
/// // Load this too
/// "{some_text}"
/// }
/// }
/// };
/// ```
#[allow(unused)]
fn load_placeholders(
+21 -13
View File
@@ -153,13 +153,15 @@ impl ErrorBoundary {
///
/// The call stack is saved for this component and provided to the error boundary
///
/// ```rust, ignore
/// ```rust
/// use dioxus::prelude::*;
///
/// #[component]
/// fn app(count: String) -> Element {
/// let id: i32 = count.parse().throw()?;
/// let count: i32 = count.parse().throw()?;
///
/// rsx! {
/// div { "Count {}" }
/// div { "Count {count}" }
/// }
/// }
/// ```
@@ -178,13 +180,15 @@ pub trait Throw<S = ()>: Sized {
/// which is what this trait shells out to.
///
///
/// ```rust, ignore
/// ```rust
/// use dioxus::prelude::*;
///
/// #[component]
/// fn app( count: String) -> Element {
/// let id: i32 = count.parse().throw()?;
/// let count: i32 = count.parse().throw()?;
///
/// rsx! {
/// div { "Count {}" }
/// div { "Count {count}" }
/// }
/// }
/// ```
@@ -201,13 +205,15 @@ pub trait Throw<S = ()>: Sized {
/// which is what this trait shells out to.
///
///
/// ```rust, ignore
/// ```rust
/// use dioxus::prelude::*;
///
/// #[component]
/// fn app( count: String) -> Element {
/// let id: i32 = count.parse().throw()?;
/// let count: i32 = count.parse().throw()?;
///
/// rsx! {
/// div { "Count {}" }
/// div { "Count {count}" }
/// }
/// }
/// ```
@@ -447,13 +453,15 @@ impl<
///
/// ## Example
///
/// ```rust, ignore
/// rsx!{
/// ```rust
/// # use dioxus::prelude::*;
/// # fn ThrowsError() -> Element { todo!() }
/// rsx! {
/// ErrorBoundary {
/// handle_error: |error| rsx! { "Oops, we encountered an error. Please report {error} to the developer of this application" }
/// handle_error: |error| rsx! { "Oops, we encountered an error. Please report {error} to the developer of this application" },
/// ThrowsError {}
/// }
/// }
/// };
/// ```
///
/// ## Usage
+44 -36
View File
@@ -12,15 +12,15 @@ use std::{
///
/// # Example
///
/// ```rust, ignore
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// rsx! {
/// button {
/// onclick: move |evt: Event<MouseData>| {
/// evt.cancel_bubble();
///
/// evt.stop_propagation();
/// }
/// }
/// }
/// };
/// ```
pub struct Event<T: 'static + ?Sized> {
/// The data associated with this event
@@ -42,15 +42,16 @@ impl<T> Event<T> {
///
/// # Example
///
/// ```rust, ignore
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// rsx! {
/// button {
/// onclick: move |evt: Event<FormData>| {
/// let data = evt.map(|data| data.value());
/// assert_eq!(data.inner(), "hello world");
/// onclick: move |evt: MouseEvent| {
/// let data = evt.map(|data| data.client_coordinates());
/// println!("{:?}", data.data());
/// }
/// }
/// }
/// };
/// ```
pub fn map<U: 'static, F: FnOnce(&T) -> U>(&self, f: F) -> Event<U> {
Event {
@@ -63,14 +64,16 @@ impl<T> Event<T> {
///
/// # Example
///
/// ```rust, ignore
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// rsx! {
/// button {
/// onclick: move |evt: Event<MouseData>| {
/// # #[allow(deprecated)]
/// evt.cancel_bubble();
/// }
/// }
/// }
/// };
/// ```
#[deprecated = "use stop_propagation instead"]
pub fn cancel_bubble(&self) {
@@ -81,14 +84,15 @@ impl<T> Event<T> {
///
/// # Example
///
/// ```rust, ignore
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// rsx! {
/// button {
/// onclick: move |evt: Event<MouseData>| {
/// evt.stop_propagation();
/// }
/// }
/// }
/// };
/// ```
pub fn stop_propagation(&self) {
self.propagates.set(false);
@@ -96,17 +100,18 @@ impl<T> Event<T> {
/// Get a reference to the inner data from this event
///
/// ```rust, ignore
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// rsx! {
/// button {
/// onclick: move |evt: Event<MouseData>| {
/// let data = evt.inner.clone();
/// cx.spawn(async move {
/// let data = evt.data();
/// async move {
/// println!("{:?}", data);
/// });
/// }
/// }
/// }
/// }
/// };
/// ```
pub fn data(&self) -> Rc<T> {
self.data.clone()
@@ -145,12 +150,13 @@ impl<T: std::fmt::Debug> std::fmt::Debug for Event<T> {
///
/// # Example
///
/// ```rust, ignore
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// rsx!{
/// MyComponent { onclick: move |evt| tracing::debug!("clicked") }
/// }
/// };
///
/// #[derive(Props)]
/// #[derive(Props, Clone, PartialEq)]
/// struct MyProps {
/// onclick: EventHandler<MouseEvent>,
/// }
@@ -162,26 +168,28 @@ impl<T: std::fmt::Debug> std::fmt::Debug for Event<T> {
/// }
/// }
/// }
///
/// ```
pub struct EventHandler<T = ()> {
pub(crate) origin: ScopeId,
// During diffing components with EventHandler, we move the EventHandler over in place instead of rerunning the child component.
// ```rust
// #[component]
// fn Child(onclick: EventHandler<MouseEvent>) -> Element {
// rsx!{
// button {
// // Diffing Child will not rerun this component, it will just update the EventHandler in place so that if this callback is called, it will run the latest version of the callback
// onclick: move |evt| cx.onclick.call(evt),
// }
// }
/// During diffing components with EventHandler, we move the EventHandler over in place instead of rerunning the child component.
///
/// ```rust
/// # use dioxus::prelude::*;
/// #[component]
/// fn Child(onclick: EventHandler<MouseEvent>) -> Element {
/// rsx!{
/// button {
/// // Diffing Child will not rerun this component, it will just update the EventHandler in place so that if this callback is called, it will run the latest version of the callback
/// onclick: move |evt| onclick(evt),
/// }
/// }
/// }
/// ```
// This is both more efficient and allows us to avoid out of date EventHandlers.
//
// We double box here because we want the data to be copy (GenerationalBox) and still update in place (ExternalListenerCallback)
// This isn't an ideal solution for performance, but it is non-breaking and fixes the issues described in https://github.com/DioxusLabs/dioxus/pull/2298
///
/// This is both more efficient and allows us to avoid out of date EventHandlers.
///
/// We double box here because we want the data to be copy (GenerationalBox) and still update in place (ExternalListenerCallback)
/// This isn't an ideal solution for performance, but it is non-breaking and fixes the issues described in <https://github.com/DioxusLabs/dioxus/pull/2298>
pub(super) callback: GenerationalBox<Option<ExternalListenerCallback<T>>>,
}
+12 -13
View File
@@ -11,10 +11,12 @@ use crate::innerlude::*;
///
/// ## Example
///
/// ```rust, ignore
/// ```rust
/// # use dioxus::prelude::*;
/// let value = 1;
/// rsx!{
/// Fragment { key: "abc" }
/// }
/// Fragment { key: "{value}" }
/// };
/// ```
///
/// ## Usage
@@ -61,28 +63,25 @@ impl<const A: bool> FragmentBuilder<A> {
///
/// ## Example
///
/// ```rust, ignore
/// ```rust
/// # use dioxus::prelude::*;
/// fn app() -> Element {
/// rsx!{
/// CustomCard {
/// h1 {}
/// p {}
/// }
/// })
/// }
/// }
///
/// #[derive(PartialEq, Props)]
/// struct CardProps {
/// children: Element
/// }
///
/// fn CustomCard(cx: CardProps) -> Element {
/// #[component]
/// fn CustomCard(children: Element) -> Element {
/// rsx!{
/// div {
/// h1 {"Title card"}
/// {cx.children}
/// {children}
/// }
/// })
/// }
/// }
/// ```
impl Properties for FragmentProps {
+81 -12
View File
@@ -81,16 +81,39 @@ pub fn suspend(task: Task) -> Element {
/// }
/// });
/// ```
///
#[doc = include_str!("../docs/common_spawn_errors.md")]
pub fn spawn_isomorphic(fut: impl Future<Output = ()> + 'static) -> Task {
Runtime::with_current_scope(|cx| cx.spawn_isomorphic(fut)).expect("to be in a dioxus runtime")
}
/// Spawns the future but does not return the [`TaskId`]
/// Spawns the future but does not return the [`Task`]. This task will automatically be canceled when the component is dropped.
///
/// # Example
/// ```rust
/// use dioxus::prelude::*;
///
/// fn App() -> Element {
/// rsx! {
/// button {
/// onclick: move |_| {
/// spawn(async move {
/// tokio::time::sleep(std::time::Duration::from_secs(1)).await;
/// println!("Hello World");
/// });
/// },
/// "Print hello in one second"
/// }
/// }
/// }
/// ```
///
#[doc = include_str!("../docs/common_spawn_errors.md")]
pub fn spawn(fut: impl Future<Output = ()> + 'static) -> Task {
Runtime::with_current_scope(|cx| cx.spawn(fut)).expect("to be in a dioxus runtime")
}
/// Queue an effect to run after the next render
/// Queue an effect to run after the next render. You generally shouldn't need to interact with this function directly. [use_effect](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_effect.html) will call this function for you.
pub fn queue_effect(f: impl FnOnce() + 'static) {
Runtime::with_current_scope(|cx| cx.queue_effect(f)).expect("to be in a dioxus runtime")
}
@@ -100,6 +123,53 @@ pub fn queue_effect(f: impl FnOnce() + 'static) {
/// This is good for tasks that need to be run after the component has been dropped.
///
/// **This will run the task in the root scope. Any calls to global methods inside the future (including `context`) will be run in the root scope.**
///
/// # Example
///
/// ```rust
/// use dioxus::prelude::*;
///
/// // The parent component can create and destroy children dynamically
/// fn App() -> Element {
/// let mut count = use_signal(|| 0);
///
/// rsx! {
/// button {
/// onclick: move |_| count += 1,
/// "Increment"
/// }
/// button {
/// onclick: move |_| count -= 1,
/// "Decrement"
/// }
///
/// for id in 0..10 {
/// Child { id }
/// }
/// }
/// }
///
/// #[component]
/// fn Child(id: i32) -> Element {
/// rsx! {
/// button {
/// onclick: move |_| {
/// // This will spawn a task in the root scope that will run forever
/// // It will keep running even if you drop the child component by decreasing the count
/// spawn_forever(async move {
/// loop {
/// tokio::time::sleep(std::time::Duration::from_secs(1)).await;
/// println!("Running task spawned in child component {id}");
/// }
/// });
/// },
/// "Spawn background task"
/// }
/// }
/// }
/// ```
///
#[doc = include_str!("../docs/common_spawn_errors.md")]
pub fn spawn_forever(fut: impl Future<Output = ()> + 'static) -> Option<Task> {
Runtime::with_scope(ScopeId::ROOT, |cx| cx.spawn(fut))
}
@@ -203,25 +273,23 @@ pub fn schedule_update_any() -> Arc<dyn Fn(ScopeId) + Send + Sync> {
/// (created with [`use_effect`](crate::use_effect)).
///
/// Example:
/// ```rust, ignore
/// ```rust
/// use dioxus::prelude::*;
///
/// fn app() -> Element {
/// let state = use_signal(|| true);
/// let mut state = use_signal(|| true);
/// rsx! {
/// for _ in 0..100 {
/// h1 {
/// "spacer"
/// }
/// }
/// if **state {
/// rsx! {
/// child_component {}
/// }
/// if state() {
/// child_component {}
/// }
/// button {
/// onclick: move |_| {
/// state.set(!*state.get());
/// state.toggle()
/// },
/// "Unmount element"
/// }
@@ -229,9 +297,9 @@ pub fn schedule_update_any() -> Arc<dyn Fn(ScopeId) + Send + Sync> {
/// }
///
/// fn child_component() -> Element {
/// let original_scroll_position = use_signal(|| 0.0);
/// let mut original_scroll_position = use_signal(|| 0.0);
///
/// use_effect(move |_| async move {
/// use_effect(move || {
/// let window = web_sys::window().unwrap();
/// let document = window.document().unwrap();
/// let element = document.get_element_by_id("my_element").unwrap();
@@ -242,7 +310,7 @@ pub fn schedule_update_any() -> Arc<dyn Fn(ScopeId) + Send + Sync> {
/// use_drop(move || {
/// /// restore scroll to the top of the page
/// let window = web_sys::window().unwrap();
/// window.scroll_with_x_and_y(*original_scroll_position.current(), 0.0);
/// window.scroll_with_x_and_y(original_scroll_position(), 0.0);
/// });
///
/// rsx!{
@@ -253,6 +321,7 @@ pub fn schedule_update_any() -> Arc<dyn Fn(ScopeId) + Send + Sync> {
/// }
/// }
/// ```
#[doc(alias = "use_on_unmount")]
pub fn use_drop<D: FnOnce() + 'static>(destroy: D) {
struct LifeCycle<D: FnOnce()> {
/// Wrap the closure in an option so that we can take it out on drop.
+1 -1
View File
@@ -42,7 +42,7 @@ pub(crate) mod innerlude {
pub use crate::tasks::*;
pub use crate::virtual_dom::*;
/// An [`Element`] is a possibly-none [`VNode`] created by calling `render` on [`Scope`] or [`ScopeState`].
/// An [`Element`] is a possibly-none [`VNode`] created by calling `render` on [`ScopeId`] or [`ScopeState`].
///
/// An Errored [`Element`] will propagate the error to the nearest error boundary.
pub type Element = Option<VNode>;
+15 -8
View File
@@ -101,18 +101,25 @@ pub struct VNodeInner {
/// The inner list *must* be in the format [static named attributes, remaining dynamically named attributes].
///
/// For example:
/// ```rust, ignore
/// div {
/// class: "{class}",
/// ..attrs,
/// p {
/// color: "{color}",
/// ```rust
/// # use dioxus::prelude::*;
/// let class = "my-class";
/// let attrs = vec![];
/// let color = "red";
///
/// rsx! {
/// div {
/// class: "{class}",
/// ..attrs,
/// p {
/// color: "{color}",
/// }
/// }
/// }
/// };
/// ```
///
/// Would be represented as:
/// ```rust, ignore
/// ```text
/// [
/// [class, every attribute in attrs sorted by name], // Slot 0 in the template
/// [color], // Slot 1 in the template
+55 -11
View File
@@ -4,25 +4,47 @@ use crate::innerlude::*;
/// 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.
/// derive macro to implement the `Properties` trait automatically.
///
/// If your props are 'static, then Dioxus will require that they also be PartialEq for the derived memoize strategy.
///
/// 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.
/// Dioxus requires your props to be 'static, `Clone`, and `PartialEq`. We use the `PartialEq` trait to determine if
/// the props have changed when we diff the component.
///
/// ## Example
///
/// For props that are 'static:
/// ```rust, ignore
/// ```rust
/// # use dioxus::prelude::*;
/// #[derive(Props, PartialEq, Clone)]
/// struct MyProps {
/// struct MyComponentProps {
/// data: String
/// }
///
/// fn MyComponent(props: MyComponentProps) -> Element {
/// rsx! {
/// div { "Hello {props.data}" }
/// }
/// }
/// ```
///
/// Or even better, derive your entire props struct with the [`#[crate::component]`] macro:
///
/// ```rust
/// # use dioxus::prelude::*;
/// #[component]
/// fn MyComponent(data: String) -> Element {
/// rsx! {
/// div { "Hello {data}" }
/// }
/// }
/// ```
#[rustversion::attr(
since(1.78.0),
diagnostic::on_unimplemented(
message = "`Props` is not implemented for `{Self}`",
label = "Props",
note = "Props is a trait that is automatically implemented for all structs that can be used as props for a component",
note = "If you manually created a new properties struct, you may have forgotten to add `#[derive(Props, PartialEq, Clone)]` to your struct",
)
)]
pub trait Properties: Clone + Sized + 'static {
/// The type of the builder for this component.
/// Used to create "in-progress" versions of the props.
@@ -96,6 +118,28 @@ where
}
/// Any component that implements the `ComponentFn` trait can be used as a component.
///
/// This trait is automatically implemented for functions that are in one of the following forms:
/// - `fn() -> Element`
/// - `fn(props: Properties) -> Element`
///
/// You can derive it automatically for any function with arguments that implement PartialEq with the `#[component]` attribute:
/// ```rust
/// # use dioxus::prelude::*;
/// #[component]
/// fn MyComponent(a: u32, b: u32) -> Element {
/// rsx! { "a: {a}, b: {b}" }
/// }
/// ```
#[rustversion::attr(
since(1.78.0),
diagnostic::on_unimplemented(
message = "`Component<{Props}>` is not implemented for `{Self}`",
label = "Component",
note = "Components are functions in the form `fn() -> Element`, `fn(props: Properties) -> Element`, or `#[component] fn(partial_eq1: u32, partial_eq2: u32) -> Element`.",
note = "You may have forgotten to add `#[component]` to your function to automatically implement the `ComponentFunction` trait."
)
)]
pub trait ComponentFunction<Props, Marker = ()>: Clone + 'static {
/// Get the type id of the component.
fn id(&self) -> TypeId {
+4 -3
View File
@@ -2,7 +2,7 @@
//!
//! ## Goals
//! We try to prevent three different situations:
//! 1. Running queued work after it could be dropped. Related issues (https://github.com/DioxusLabs/dioxus/pull/1993)
//! 1. Running queued work after it could be dropped. Related issues (<https://github.com/DioxusLabs/dioxus/pull/1993>)
//!
//! User code often assumes that this property is true. For example, if this code reruns the child component after signal is changed to None, it will panic
//! ```rust, ignore
@@ -23,7 +23,7 @@
//! }
//! ```
//!
//! 2. Running effects before the dom is updated. Related issues (https://github.com/DioxusLabs/dioxus/issues/2307)
//! 2. Running effects before the dom is updated. Related issues (<https://github.com/DioxusLabs/dioxus/issues/2307>)
//!
//! Effects can be used to run code that accesses the DOM directly. They should only run when the DOM is in an updated state. If they are run with an out of date version of the DOM, unexpected behavior can occur.
//! ```rust, ignore
@@ -39,8 +39,9 @@
//! div { id: "{id}" }
//! }
//! }
//! ```
//!
//! 3. Observing out of date state. Related issues (https://github.com/DioxusLabs/dioxus/issues/1935)
//! 3. Observing out of date state. Related issues (<https://github.com/DioxusLabs/dioxus/issues/1935>)
//!
//! Where ever possible, updates should happen in an order that makes it impossible to observe an out of date state.
//! ```rust, ignore
+12 -8
View File
@@ -173,16 +173,20 @@ impl Scope {
///
/// # Example
///
/// ```rust, ignore
/// ```rust
/// # use dioxus::prelude::*;
/// #[derive(Clone)]
/// struct SharedState(&'static str);
///
/// static app: Component = |cx| {
/// cx.use_hook(|| cx.provide_context(SharedState("world")));
/// // The parent provides context that is available in all children
/// fn app() -> Element {
/// use_hook(|| provide_context(SharedState("world")));
/// rsx!(Child {})
/// }
///
/// static Child: Component = |cx| {
/// let state = cx.consume_state::<SharedState>();
/// // Any child elements can access the context with the `consume_context` function
/// fn Child() -> Element {
/// let state = use_context::<SharedState>();
/// rsx!(div { "hello {state.0}" })
/// }
/// ```
@@ -258,7 +262,7 @@ impl Scope {
id
}
/// Spawns the future but does not return the [`TaskId`]
/// Spawns the future and returns the [`Task`]
pub fn spawn(&self, fut: impl Future<Output = ()> + 'static) -> Task {
let id = Runtime::with(|rt| rt.spawn(self.id, fut)).expect("Runtime to exist");
self.spawned_tasks.borrow_mut().insert(id);
@@ -410,7 +414,7 @@ impl ScopeId {
Runtime::with_scope(self, |cx| cx.spawn(fut))
}
/// Spawns the future but does not return the [`TaskId`]
/// Spawns the future but does not return the [`Task`]
pub fn spawn(self, fut: impl Future<Output = ()> + 'static) {
Runtime::with_scope(self, |cx| cx.spawn(fut));
}
@@ -434,7 +438,7 @@ impl ScopeId {
/// Create a subscription that schedules a future render for the reference component. Unlike [`Self::needs_update`], this function will work outside of the dioxus runtime.
///
/// ## Notice: you should prefer using [`dioxus_core::schedule_update_any`] and [`Self::scope_id`]
/// ## Notice: you should prefer using [`schedule_update_any`]
pub fn schedule_update(&self) -> Arc<dyn Fn() + Send + Sync + 'static> {
Runtime::with_scope(*self, |cx| cx.schedule_update()).expect("to be in a dioxus runtime")
}
+3 -3
View File
@@ -38,9 +38,9 @@ impl ScopeId {
///
/// # Example
///
/// ```rust, ignore
/// use dioxus_signals::*;
/// let my_persistent_state = Signal::new_in_scope(ScopeId::ROOT, String::new());
/// ```rust, no_run
/// use dioxus::prelude::*;
/// let my_persistent_state = Signal::new_in_scope(String::new(), ScopeId::ROOT);
/// ```
pub const ROOT: ScopeId = ScopeId(0);
}
+1 -1
View File
@@ -240,7 +240,7 @@ impl Runtime {
poll_result
}
/// Drop the future with the given TaskId
/// Drop the future with the given Task
///
/// This does not abort the task, so you'll want to wrap it in an abort handle if that's important to you
pub(crate) fn remove_task(&self, id: Task) -> Option<Rc<LocalTask>> {
+95 -84
View File
@@ -91,6 +91,7 @@ use tracing::instrument;
///
/// ```rust
/// # use dioxus::prelude::*;
/// # use dioxus_core::*;
/// # fn app() -> Element { rsx! { div {} } }
///
/// let mut vdom = VirtualDom::new(app);
@@ -99,36 +100,67 @@ use tracing::instrument;
///
/// To call listeners inside the VirtualDom, call [`VirtualDom::handle_event`] with the appropriate event data.
///
/// ```rust, ignore
/// vdom.handle_event(event);
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # use dioxus_core::*;
/// # fn app() -> Element { rsx! { div {} } }
/// # let mut vdom = VirtualDom::new(app);
/// let event = std::rc::Rc::new(0);
/// vdom.handle_event("onclick", event, ElementId(0), true);
/// ```
///
/// While no events are ready, call [`VirtualDom::wait_for_work`] to poll any futures inside the VirtualDom.
///
/// ```rust, ignore
/// vdom.wait_for_work().await;
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # use dioxus_core::*;
/// # fn app() -> Element { rsx! { div {} } }
/// # let mut vdom = VirtualDom::new(app);
/// tokio::runtime::Runtime::new().unwrap().block_on(async {
/// vdom.wait_for_work().await;
/// });
/// ```
///
/// Once work is ready, call [`VirtualDom::render_with_deadline`] to compute the differences between the previous and
/// current UI trees. This will return a [`Mutations`] object that contains Edits, Effects, and NodeRefs that need to be
/// Once work is ready, call [`VirtualDom::render_immediate`] to compute the differences between the previous and
/// current UI trees. This will write edits to a [`WriteMutations`] object you pass in that contains with edits that need to be
/// handled by the renderer.
///
/// ```rust, ignore
/// let mutations = vdom.work_with_deadline(tokio::time::sleep(Duration::from_millis(100)));
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # use dioxus_core::*;
/// # fn app() -> Element { rsx! { div {} } }
/// # let mut vdom = VirtualDom::new(app);
/// let mut mutations = Mutations::default();
///
/// for edit in mutations.edits {
/// real_dom.apply(edit);
/// }
/// vdom.render_immediate(&mut mutations);
/// ```
///
/// To not wait for suspense while diffing the VirtualDom, call [`VirtualDom::render_immediate`] or pass an immediately
/// ready future to [`VirtualDom::render_with_deadline`].
/// To not wait for suspense while diffing the VirtualDom, call [`VirtualDom::render_immediate`].
///
///
/// ## Building an event loop around Dioxus:
///
/// Putting everything together, you can build an event loop around Dioxus by using the methods outlined above.
/// ```rust, ignore
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # use dioxus_core::*;
/// # struct RealDom;
/// # struct Event {}
/// # impl RealDom {
/// # fn new() -> Self {
/// # Self {}
/// # }
/// # fn apply(&mut self) -> Mutations {
/// # todo!()
/// # }
/// # async fn wait_for_event(&mut self) -> std::rc::Rc<dyn std::any::Any> {
/// # todo!()
/// # }
/// # }
/// #
/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
/// let mut real_dom = RealDom::new();
///
/// #[component]
/// fn app() -> Element {
/// rsx! {
@@ -136,51 +168,39 @@ use tracing::instrument;
/// }
/// }
///
/// let dom = VirtualDom::new(app);
/// let mut dom = VirtualDom::new(app);
///
/// dom.rebuild(real_dom.apply());
/// dom.rebuild(&mut real_dom.apply());
///
/// loop {
/// select! {
/// tokio::select! {
/// _ = dom.wait_for_work() => {}
/// evt = real_dom.wait_for_event() => dom.handle_event(evt),
/// evt = real_dom.wait_for_event() => dom.handle_event("onclick", evt, ElementId(0), true),
/// }
///
/// real_dom.apply(dom.render_immediate());
/// dom.render_immediate(&mut real_dom.apply());
/// }
/// # });
/// ```
///
/// ## Waiting for suspense
///
/// Because Dioxus supports suspense, you can use it for server-side rendering, static site generation, and other usecases
/// Because Dioxus supports suspense, you can use it for server-side rendering, static site generation, and other use cases
/// where waiting on portions of the UI to finish rendering is important. To wait for suspense, use the
/// [`VirtualDom::render_with_deadline`] method:
/// [`VirtualDom::wait_for_suspense`] method:
///
/// ```rust, ignore
/// let dom = VirtualDom::new(app);
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # use dioxus_core::*;
/// # fn app() -> Element { rsx! { div {} } }
/// tokio::runtime::Runtime::new().unwrap().block_on(async {
/// let mut dom = VirtualDom::new(app);
///
/// let deadline = tokio::time::sleep(Duration::from_millis(100));
/// let edits = dom.render_with_deadline(deadline).await;
/// ```
/// dom.rebuild_in_place();
/// dom.wait_for_suspense().await;
/// });
///
/// ## Use with streaming
///
/// If not all rendering is done by the deadline, it might be worthwhile to stream the rest later. To do this, we
/// suggest rendering with a deadline, and then looping between [`VirtualDom::wait_for_work`] and render_immediate until
/// no suspended work is left.
///
/// ```rust, ignore
/// let dom = VirtualDom::new(app);
///
/// let deadline = tokio::time::sleep(Duration::from_millis(20));
/// let edits = dom.render_with_deadline(deadline).await;
///
/// real_dom.apply(edits);
///
/// while dom.has_suspended_work() {
/// dom.wait_for_work().await;
/// real_dom.apply(dom.render_immediate());
/// }
/// // Render the virtual dom
/// ```
pub struct VirtualDom {
pub(crate) scopes: Slab<ScopeState>,
@@ -217,7 +237,9 @@ impl VirtualDom {
///
///
/// # Example
/// ```rust, ignore
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # use dioxus_core::*;
/// fn Example() -> Element {
/// rsx!( div { "hello world" } )
/// }
@@ -241,8 +263,10 @@ impl VirtualDom {
///
///
/// # Example
/// ```rust, ignore
/// #[derive(PartialEq, Props)]
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # use dioxus_core::*;
/// #[derive(PartialEq, Props, Clone)]
/// struct SomeProps {
/// name: &'static str
/// }
@@ -251,12 +275,21 @@ impl VirtualDom {
/// rsx!{ div { "hello {cx.name}" } }
/// }
///
/// let dom = VirtualDom::new(Example);
/// let dom = VirtualDom::new_with_props(Example, SomeProps { name: "world" });
/// ```
///
/// Note: the VirtualDom is not progressed on creation. You must either "run_with_deadline" or use "rebuild" to progress it.
///
/// ```rust, ignore
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # use dioxus_core::*;
/// # #[derive(PartialEq, Props, Clone)]
/// # struct SomeProps {
/// # name: &'static str
/// # }
/// # fn Example(cx: SomeProps) -> Element {
/// # rsx!{ div { "hello {cx.name}" } }
/// # }
/// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
/// dom.rebuild_in_place();
/// ```
@@ -274,36 +307,7 @@ impl VirtualDom {
dom
}
/// Create a new VirtualDom with the given properties for the root component.
///
/// # Description
///
/// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders.
///
/// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
/// to toss out the entire tree.
///
///
/// # Example
/// ```rust, ignore
/// #[derive(PartialEq, Props)]
/// struct SomeProps {
/// name: &'static str
/// }
///
/// fn Example(cx: SomeProps) -> Element {
/// rsx!{ div{ "hello {cx.name}" } }
/// }
///
/// let dom = VirtualDom::new(Example);
/// ```
///
/// Note: the VirtualDom is not progressed on creation. You must either "run_with_deadline" or use "rebuild" to progress it.
///
/// ```rust, ignore
/// let mut dom = VirtualDom::new_from_root(VComponent::new(Example, SomeProps { name: "jane" }, "Example"));
/// dom.rebuild(to_writer);
/// ```
/// Create a new VirtualDom from something that implements [`AnyProps`]
#[instrument(skip(root), level = "trace", name = "VirtualDom::new")]
pub(crate) fn new_with_component(root: impl AnyProps + 'static) -> Self {
let (tx, rx) = futures_channel::mpsc::unbounded();
@@ -448,7 +452,9 @@ impl VirtualDom {
///
/// # Example
///
/// ```rust, ignore
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # fn app() -> Element { rsx! { div {} } }
/// let dom = VirtualDom::new(app);
/// ```
#[instrument(skip(self), level = "trace", name = "VirtualDom::wait_for_work")]
@@ -624,11 +630,16 @@ impl VirtualDom {
/// Any templates previously registered will remain.
///
/// # Example
/// ```rust, ignore
/// static app: Component = |cx| rsx!{ "hello world" };
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// # use dioxus_core::*;
/// fn app() -> Element {
/// rsx! { "hello world" }
/// }
///
/// let mut dom = VirtualDom::new();
/// dom.rebuild(to_writer);
/// let mut dom = VirtualDom::new(app);
/// let mut mutations = Mutations::default();
/// dom.rebuild(&mut mutations);
/// ```
#[instrument(skip(self, to), level = "trace", name = "VirtualDom::rebuild")]
pub fn rebuild(&mut self, to: &mut impl WriteMutations) {