user-interfacerustdioxus

Mutating a dioxus UseState struct that doesn't implement clone


I'm using Dioxus to build a UI. This UI relies heavily on a struct from a third-party library. This struct doesn't implement clone. The struct needs to be modified on button press. Consider this simplified example:

use dioxus::prelude::*;

struct ThirdPartyStruct(i64);

impl ThirdPartyStruct {
    fn add_one(&mut self) {
        self.0 += 1;
    }
}

fn app(cx: Scope) -> Element {
    let third_party_struct = use_state(cx, || ThirdPartyStruct(0));

    cx.render(rsx! {
        button {
            onclick: move |_| third_party_struct.make_mut().add_one(),
            "Click me!"
        }
        p {
            "{third_party_struct.get().0}"
        }
    })
}

This does not compile, since make_mut requires clone to be implemented for ThirdPartyStruct. The real ThirdPartyStruct isn't nearly as simple and the real equivalent of add_one has to be used (so solutions like "use + 1 instead" are invalid). Implementing clone by hand would also be infeasible.

I have tried many things, including most of the methods for the UseState struct meant for modifying the contained struct. All of them either require the struct to be modified by value (like calling + 1 instead of += 1) or need to have clone implemented.


Solution

  • The only way I can think of resolving this is wrapping the struct in a RefCell. It might not be the most memory safe way, but it works.

    The side effect of this is the state does not know it has changed, so you will need to manually mark the component as dirty each time you call a mutating function with cx.needs_update().

    fn app(cx: Scope) -> Element {
        let third_party_struct = use_state(cx, || RefCell::new(ThirdPartyStruct(0)));
    
        cx.render(rsx! {
            button {
                onclick: move |_| {
                    third_party_struct.borrow_mut().add_one();
                    cx.needs_update();
                },
                "Click me!"
            }
            p {
                "{third_party_struct.get().borrow().0}"
            }
        })
    }
    

    It's worth noting I have not tried this with the sysinfo crate, but it works with your MRE.