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

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.

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();

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);

Get the Scope for the root component.

This is useful for traversing the tree from the root for heuristics or alternative renderers that use Dioxus directly.

This method is equivalent to calling get_scope(ScopeId(0))

Example
let mut dom = VirtualDom::new(example);
dom.rebuild();

Get the ScopeState for a component given its ScopeId

Example

Get an UnboundedSender handle to the channel used by the scheduler.

Example
let dom = VirtualDom::new(App);
let sender = dom.get_scheduler_channel();

Try to get an element from its ElementId

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)));

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());

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();

Manually kick the VirtualDom to process any

Handle an individual message for the scheduler.

This will either call an event listener or mark a component as dirty.

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);
}

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);

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();

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"));

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"));

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"));

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

Executes the destructor for this type. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.