-
Hi all, First off - loving Working on a proof of concept ahead of migrating a larger web app. In this app, modals (dialogs) are managed at the app top-level so that they stack up in a predictable order and the stack can be closed all at once. It works nicely in React. In React, this was a In In the meantime, I'll fall back to component-local modals and see if this becomes more clear as I get used to Is there a pattern you can suggest? I've tried: Lifetime covariant with type ElementBuilder<'e> = Box<dyn Fn(Scope<'e>) -> Element<'e>>;
#[derive(Default)]
pub struct ModalContext<'e> {
pub open_modals: Vec<ElementBuilder<'e>>,
}
impl<'e> ModalContext<'e> {
pub fn open_modal(&mut self, modal: ElementBuilder<'e>) {
self.open_modals.push(modal);
}
}
type ElementBuilder = Box<dyn Fn() -> Element<'static>>;
#[derive(Default)]
pub struct ModalContext {
pub open_modals: Vec<ElementBuilder>,
}
impl ModalContext {
pub fn open_modal(&mut self, modal: ElementBuilder) {
self.open_modals.push(modal);
}
} Storing Elements rather than element builders (which AFAICT can't work, but worth a shot): #[derive(Default)]
pub struct ModalContext<'e> {
pub open_modals: Vec<Element<'e>>,
}
impl<'e> ModalContext<'e> {
pub fn open_modal(&mut self, modal: Element<'e>) {
self.open_modals.push(modal);
}
} All of those compile on their own, of course, but fail when used in a context like (don't mind the details here too much, it's a sketch):
And, while this isn't an HOC, it seems relevant to that common pattern in React (legacy docs), and is something that folks (like me) might naturally try to port over. Thanks! And sorry if this is in the docs, issues, or discussions - I searched thoroughly. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
You were pretty close on the first try. Components cannot be closures (if this is an issue you can work around it with a wrapper component that takes a use dioxus::prelude::*;
fn main() {
dioxus_desktop::launch(app);
}
/// An element builder is a function that takes a scope of any lifetime and returns an element with the same lifetime.
type ElementBuilder = for<'e> fn(Scope<'e>) -> Element<'e>;
#[derive(Default)]
pub struct ModalContext {
pub open_modals: Vec<ElementBuilder>,
}
impl ModalContext {
pub fn open_modal(&mut self, modal: ElementBuilder) {
self.open_modals.push(modal);
}
}
fn app(cx: Scope) -> Element {
use_shared_state_provider::<ModalContext>(cx, Default::default);
let modal_context = use_shared_state::<ModalContext>(cx).unwrap();
cx.render(rsx! {
for Modal in modal_context.read().open_modals.iter().copied() {
rsx! { div { Modal {} } }
}
Child {}
})
}
fn Child(cx: Scope) -> Element {
let modal_context = use_shared_state::<ModalContext>(cx).unwrap();
cx.render(rsx! {
div {
button {
onclick: move |_| {
modal_context.write().open_modal(Modal);
},
"Open modal"
}
}
})
}
fn Modal(cx: Scope) -> Element {
let modal_context = use_shared_state::<ModalContext>(cx).unwrap();
cx.render(rsx! {
div {
"Modal"
button {
onclick: move |_| {
modal_context.write().open_modals.pop();
},
"Close"
}
}
})
} |
Beta Was this translation helpful? Give feedback.
You were pretty close on the first try. Components cannot be closures (if this is an issue you can work around it with a wrapper component that takes a
Fn
component and calls it). You can use higher bounded lifetimes to get rid of the'e
lifetime in ModalContext. Here is a working example: