Function dioxus_hooks::use_ref

source ·
pub fn use_ref<T: 'static>(
    cx: &ScopeState,
    initialize_refcell: impl FnOnce() -> T
) -> &UseRef<T>
Expand description

use_ref is a key foundational hook for storing state in Dioxus.

It is different that use_state in that the value stored is not “immutable”. Instead, UseRef is designed to store larger values that will be mutated at will.

Writing Values

Generally, use_ref is just a wrapper around a RefCell that tracks mutable writes through the write method. Whenever write is called, the component that initialized the hook will be marked as “dirty”.

let val = use_ref(|| HashMap::<u32, String>::new());

// using `write` will give us a `RefMut` to the inner value, which we can call methods on
// This marks the component as "dirty"
val.write().insert(1, "hello".to_string());

You can avoid this default behavior with write_silent

// with `write_silent`, the component will not be re-rendered
val.write_silent().insert(2, "goodbye".to_string());

Reading Values

To read values out of the refcell, you can use the read method which will retrun a Ref.

let map: Ref<_> = val.read();

let item = map.get(&1);

To get an &T out of the RefCell, you need to “reborrow” through the Ref:

let read = val.read();
let map = &*read;

Collections and iteration

A common usecase for use_ref is to store a large amount of data in a component. Typically this will be a collection like a HashMap or a Vec. To create new elements from the collection, we can use read() directly in our rsx!.

rsx!{
    val.read().iter().map(|(k, v)| {
        rsx!{ key: "{k}", "{v}" }
    })
}

If you are generating elements outside of rsx! then you might need to call “render” inside the iterator. For some cases you might need to collect into a temporary Vec.

let items = val.read().iter().map(|(k, v)| {
    cx.render(rsx!{ key: "{k}", "{v}" })
});

// collect into a Vec

let items: Vec<Element> = items.collect();

Use in Async

To access values from a UseRef in an async context, you need to detach it from the current scope’s lifetime, making it a 'static value. This is done by simply calling to_owned or clone.

let val = use_ref(|| HashMap::<u32, String>::new());

cx.spawn({
    let val = val.clone();
    async move {
        some_work().await;
        val.write().insert(1, "hello".to_string());
    }
})

If you’re working with lots of values like UseState and UseRef, you can use the to_owned! macro to make it easier to write the above code.

let val1 = use_ref(|| HashMap::<u32, String>::new());
let val2 = use_ref(|| HashMap::<u32, String>::new());
let val3 = use_ref(|| HashMap::<u32, String>::new());

cx.spawn({
    to_owned![val1, val2, val3];
    async move {
        some_work().await;
        val.write().insert(1, "hello".to_string());
    }
})