Struct dioxus_core::VirtualDom
source · [−]pub struct VirtualDom { /* private fields */ }
Expand description
A virtual node system that progresses user events and diffs UI trees.
Guide
Components are defined as simple functions that take Scope
and return an Element
.
#[derive(Props, PartialEq)]
struct AppProps {
title: String
}
fn App(cx: Scope<AppProps>) -> Element {
cx.render(rsx!(
div {"hello, {cx.props.title}"}
))
}
Components may be composed to make complex apps.
fn App(cx: Scope<AppProps>) -> Element {
cx.render(rsx!(
NavBar { routes: ROUTES }
Title { "{cx.props.title}" }
Footer {}
))
}
To start an app, create a VirtualDom
and call VirtualDom::rebuild
to get the list of edits required to
draw the UI.
let mut vdom = VirtualDom::new(App);
let edits = vdom.rebuild();
To inject UserEvents into the VirtualDom, call VirtualDom::get_scheduler_channel
to get access to the scheduler.
let channel = vdom.get_scheduler_channel();
channel.send_unbounded(SchedulerMsg::UserEvent(UserEvent {
// ...
}))
While waiting for UserEvents to occur, call VirtualDom::wait_for_work
to poll any futures inside the VirtualDom.
vdom.wait_for_work().await;
Once work is ready, call VirtualDom::work_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
handled by the renderer.
let mutations = vdom.work_with_deadline(|| false);
for edit in mutations {
apply(edit);
}
Building an event loop around Dioxus:
Putting everything together, you can build an event loop around Dioxus by using the methods outlined above.
fn App(cx: Scope) -> Element {
cx.render(rsx!{
div { "Hello World" }
})
}
async fn main() {
let mut dom = VirtualDom::new(App);
let mut inital_edits = dom.rebuild();
apply_edits(inital_edits);
loop {
dom.wait_for_work().await;
let frame_timeout = TimeoutFuture::new(Duration::from_millis(16));
let deadline = || (&mut frame_timeout).now_or_never();
let edits = dom.run_with_deadline(deadline).await;
apply_edits(edits);
}
}
Implementations
sourceimpl VirtualDom
impl VirtualDom
sourcepub fn new(root: Component) -> Self
pub fn new(root: Component) -> Self
Create a new VirtualDom with a component that does not have special props.
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
fn Example(cx: Scope) -> Element {
cx.render(rsx!( div { "hello world" } ))
}
let dom = VirtualDom::new(Example);
Note: the VirtualDom is not progressed, you must either “run_with_deadline” or use “rebuild” to progress it.
sourcepub fn new_with_props<P>(root: Component<P>, root_props: P) -> Self where
P: 'static,
pub fn new_with_props<P>(root: Component<P>, root_props: P) -> Self where
P: 'static,
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
#[derive(PartialEq, Props)]
struct SomeProps {
name: &'static str
}
fn Example(cx: Scope<SomeProps>) -> Element {
cx.render(rsx!{ div{ "hello {cx.props.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.
let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
let mutations = dom.rebuild();
sourcepub fn new_with_props_and_scheduler<P: 'static>(
root: Component<P>,
root_props: P,
channel: (UnboundedSender<SchedulerMsg>, UnboundedReceiver<SchedulerMsg>)
) -> Self
pub fn new_with_props_and_scheduler<P: 'static>(
root: Component<P>,
root_props: P,
channel: (UnboundedSender<SchedulerMsg>, UnboundedReceiver<SchedulerMsg>)
) -> Self
Launch the VirtualDom, but provide your own channel for receiving and sending messages into the scheduler
This is useful when the VirtualDom must be driven from outside a thread and it doesn’t make sense to wait for the VirtualDom to be created just to retrieve its channel receiver.
let channel = futures_channel::mpsc::unbounded();
let dom = VirtualDom::new_with_scheduler(Example, (), channel);
sourcepub fn base_scope(&self) -> &ScopeState
pub fn base_scope(&self) -> &ScopeState
sourcepub fn get_scope(&self, id: ScopeId) -> Option<&ScopeState>
pub fn get_scope(&self, id: ScopeId) -> Option<&ScopeState>
Get the ScopeState
for a component given its ScopeId
Example
sourcepub fn get_scheduler_channel(&self) -> UnboundedSender<SchedulerMsg>
pub fn get_scheduler_channel(&self) -> UnboundedSender<SchedulerMsg>
Get an UnboundedSender
handle to the channel used by the scheduler.
Example
let dom = VirtualDom::new(App);
let sender = dom.get_scheduler_channel();
sourcepub fn get_element(&self, id: ElementId) -> Option<&VNode<'_>>
pub fn get_element(&self, id: ElementId) -> Option<&VNode<'_>>
Try to get an element from its ElementId
sourcepub fn handle_message(&mut self, msg: SchedulerMsg)
pub fn handle_message(&mut self, msg: SchedulerMsg)
Add a new message to the scheduler queue directly.
This method makes it possible to send messages to the scheduler from outside the VirtualDom without having to
call get_schedule_channel
and then send
.
Example
let dom = VirtualDom::new(App);
dom.handle_message(SchedulerMsg::Immediate(ScopeId(0)));
sourcepub fn has_work(&self) -> bool
pub fn has_work(&self) -> bool
Check if the VirtualDom
has any pending updates or work to be done.
Example
let dom = VirtualDom::new(App);
// the dom is "dirty" when it is started and must be rebuilt to get the first render
assert!(dom.has_any_work());
sourcepub async fn wait_for_work(&mut self)
pub async fn wait_for_work(&mut self)
Wait for the scheduler to have any work.
This method polls the internal future queue and the scheduler channel. To add work to the VirtualDom, insert a message via the scheduler channel.
This lets us poll async tasks during idle periods without blocking the main thread.
Example
let dom = VirtualDom::new(App);
let sender = dom.get_scheduler_channel();
sourcepub fn process_all_messages(&mut self)
pub fn process_all_messages(&mut self)
Manually kick the VirtualDom to process any
sourcepub fn process_message(&mut self, msg: SchedulerMsg)
pub fn process_message(&mut self, msg: SchedulerMsg)
Handle an individual message for the scheduler.
This will either call an event listener or mark a component as dirty.
sourcepub fn work_with_deadline(
&mut self,
deadline: impl FnMut() -> bool
) -> Vec<Mutations<'_>>
pub fn work_with_deadline(
&mut self,
deadline: impl FnMut() -> bool
) -> Vec<Mutations<'_>>
Run the virtualdom with a deadline.
This method will perform any outstanding diffing work and try to return as many mutations as possible before the
deadline is reached. This method accepts a closure that returns true
if the deadline has been reached. To wrap
your future into a deadline, consider the now_or_never
method from future_utils
.
let mut vdom = VirtualDom::new(App);
let timeout = TimeoutFuture::from_ms(16);
let deadline = || (&mut timeout).now_or_never();
let mutations = vdom.work_with_deadline(deadline);
This method is useful when needing to schedule the virtualdom around other tasks on the main thread to prevent “jank”. It will try to finish whatever work it has by the deadline to free up time for other work.
If the work is not finished by the deadline, Dioxus will store it for later and return when work_with_deadline is called again. This means you can ensure some level of free time on the VirtualDom’s thread during the work phase.
For use in the web, it is expected that this method will be called to be executed during “idle times” and the mutations to be applied during the “paint times” IE “animation frames”. With this strategy, it is possible to craft entirely jank-free applications that perform a ton of work.
In general use, Dioxus is plenty fast enough to not need to worry about this.
Example
fn App(cx: Scope) -> Element {
cx.render(rsx!( div {"hello"} ))
}
let mut dom = VirtualDom::new(App);
loop {
let mut timeout = TimeoutFuture::from_ms(16);
let deadline = move || (&mut timeout).now_or_never();
let mutations = dom.run_with_deadline(deadline).await;
apply_mutations(mutations);
}
sourcepub fn rebuild(&mut self) -> Mutations<'_>
pub fn rebuild(&mut self) -> Mutations<'_>
Performs a full rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch.
The diff machine expects the RealDom’s stack to be the root of the application.
Tasks will not be polled with this method, nor will any events be processed from the event queue. Instead, the root component will be ran once and then diffed. All updates will flow out as mutations.
All state stored in components will be completely wiped away.
Example
static App: Component = |cx| cx.render(rsx!{ "hello world" });
let mut dom = VirtualDom::new();
let edits = dom.rebuild();
apply_edits(edits);
sourcepub fn hard_diff(&mut self, scope_id: ScopeId) -> Mutations<'_>
pub fn hard_diff(&mut self, scope_id: ScopeId) -> Mutations<'_>
Compute a manual diff of the VirtualDom between states.
This can be useful when state inside the DOM is remotely changed from the outside, but not propagated as an event.
In this case, every component will be diffed, even if their props are memoized. This method is intended to be used to force an update of the DOM when the state of the app is changed outside of the app.
To force a reflow of the entire VirtualDom, use ScopeId(0)
as the scope_id.
Example
#[derive(PartialEq, Props)]
struct AppProps {
value: Shared<&'static str>,
}
static App: Component<AppProps> = |cx| {
let val = cx.value.borrow();
cx.render(rsx! { div { "{val}" } })
};
let value = Rc::new(RefCell::new("Hello"));
let mut dom = VirtualDom::new_with_props(App, AppProps { value: value.clone(), });
let _ = dom.rebuild();
*value.borrow_mut() = "goodbye";
let edits = dom.diff();
sourcepub fn render_vnodes<'a>(
&'a self,
lazy_nodes: LazyNodes<'a, '_>
) -> &'a VNode<'a>
pub fn render_vnodes<'a>(
&'a self,
lazy_nodes: LazyNodes<'a, '_>
) -> &'a VNode<'a>
Renders an rsx
call into the Base Scope’s allocator.
Useful when needing to render nodes from outside the VirtualDom, such as in a test.
fn Base(cx: Scope) -> Element {
rsx!(cx, div {})
}
let dom = VirtualDom::new(Base);
let nodes = dom.render_nodes(rsx!("div"));
sourcepub fn diff_vnodes<'a>(
&'a self,
old: &'a VNode<'a>,
new: &'a VNode<'a>
) -> Mutations<'a>
pub fn diff_vnodes<'a>(
&'a self,
old: &'a VNode<'a>,
new: &'a VNode<'a>
) -> Mutations<'a>
Renders an rsx
call into the Base Scope’s allocator.
Useful when needing to render nodes from outside the VirtualDom, such as in a test.
fn Base(cx: Scope) -> Element {
rsx!(cx, div {})
}
let dom = VirtualDom::new(Base);
let nodes = dom.render_nodes(rsx!("div"));
sourcepub fn create_vnodes<'a>(&'a self, nodes: LazyNodes<'a, '_>) -> Mutations<'a>
pub fn create_vnodes<'a>(&'a self, nodes: LazyNodes<'a, '_>) -> Mutations<'a>
Renders an rsx
call into the Base Scope’s allocator.
Useful when needing to render nodes from outside the VirtualDom, such as in a test.
fn Base(cx: Scope) -> Element {
rsx!(cx, div {})
}
let dom = VirtualDom::new(Base);
let nodes = dom.render_nodes(rsx!("div"));
sourcepub fn diff_lazynodes<'a>(
&'a self,
left: LazyNodes<'a, '_>,
right: LazyNodes<'a, '_>
) -> (Mutations<'a>, Mutations<'a>)
pub fn diff_lazynodes<'a>(
&'a self,
left: LazyNodes<'a, '_>,
right: LazyNodes<'a, '_>
) -> (Mutations<'a>, Mutations<'a>)
Renders an rsx
call into the Base Scopes’s arena.
Useful when needing to diff two rsx! calls from outside the VirtualDom, such as in a test.
fn Base(cx: Scope) -> Element {
rsx!(cx, div {})
}
let dom = VirtualDom::new(Base);
let nodes = dom.render_nodes(rsx!("div"));
Trait Implementations
Auto Trait Implementations
impl !RefUnwindSafe for VirtualDom
impl !Send for VirtualDom
impl !Sync for VirtualDom
impl Unpin for VirtualDom
impl !UnwindSafe for VirtualDom
Blanket Implementations
sourceimpl<T> BorrowMut<T> for T where
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
const: unstable · sourcefn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more