I want to use a State
hook wrapped around a HashMap
to share the locations of Yew components. This is so that when each component is drawn and its position is set, another component will be notified (to draw lines between the components).
This is a broken example.
The idea is that on_ref_update
will be called each time a component's position changes. This will set values in the HashMap
, and this will be used to notify a listener (the connections component). I have left out the components with positions for brevity.
From what I have read this is the typical way to do it, but I get the compiler errors:
Type mismatch [E0308] expected
HashMap<String, Element>
, but foundUseStateHandle<HashMap<String, Element>>
Cannot move
But as I understand from another question (How to get the value from state in Yew Rust) I should just dereference other_node_positions
to get the state's value.
#[function_component(Pane)]
pub fn pane(PaneProps { some_prop }: &PaneProps) -> Html {
let other_node_positions: UseStateHandle<HashMap<String, Element>> = use_state(|| HashMap::new());
let on_ref_update = {
let mut other_node_positions = &other_node_positions;
Callback::from(move |(id, element): (String, Element)| {
other_node_positions.set({
other_node_positions.insert(id, element);
*other_node_positions
});
})
};
html! {
<div>
<Connections node_positions={Rc::clone(&other_node_positions)} />
</div>
}
}
There are several issues with your example:
HashMap<_, _>
but you want to pass it as a Rc<HashMap<_, _>>
to a child component via Rc::clone(&_)
.&other_node_positions
, which is borrowed data, to a Callback
that may outlive other_node_positions
.UseStateHandle
containing a non-Copy
type via *other_node_positions
.Here is one way to solve these issues:
#[function_component(Pane)]
pub fn pane() -> Html {
// Fix #1 by adding `Rc<_>` to state type.
let other_node_positions: UseStateHandle<Rc<HashMap<String, Element>>> = use_state(|| Rc::new(HashMap::new()));
let _on_ref_update = {
// Fix #2 by cloning the `UseStateHandle`.
let other_node_positions = other_node_positions.clone();
Callback::from(move |(id, element): (String, Element)| {
// Fix #3 by cloning the `HashMap<_, _>` inside the `Rc<_>` state.
let mut map = (**other_node_positions).clone();
map.insert(id, element);
other_node_positions.set(Rc::new(map));
})
};
html! {
<div>
<Connections node_positions={Rc::clone(&other_node_positions)} />
</div>
}
}