Closed mrgzi closed 6 days ago
You can use a module to group components together under a namespace:
#[allow(non_snake_case)]
mod Alert {
#[component]
pub fn Base(#[props(optional)] class: String, #[props(optional)] children: Element) -> Element {
rsx! {
div {
class: "bottom-4 right-4 flex items-center p-4 text-sm border rounded-lg bg-gray-800",
class: class,
{children}
}
}
}
#[component]
pub fn Info(#[props(optional)] classes: String, #[props(optional)] children: Element) -> Element {
rsx! {
Base {
class: "text-blue-400 border-blue-800",
div {
span { class: "font-medium", "Info! " }
{children}
}
}
}
}
#[component]
pub fn Warning(#[props(optional)] class: String, #[props(optional)] children: Element) -> Element {
rsx! {
Base {
class: "text-yellow-300 border-yellow-800",
div {
span { class: "font-medium", "Warning! " }
{children}
}
}
}
}
}
If you have a lot of shared props, you can use #[derive(Props)]
with one struct and use that struct in many components:
#[allow(non_snake_case)]
mod Alert {
#[derive(Props)]
struct AlertProps {
#[props(optional)] class: String,
#[props(optional)] children: Element
}
#[component]
pub fn Base(
props: AlertProps,
) -> Element {
rsx! {
div {
class: "bottom-4 right-4 flex items-center p-4 text-sm border rounded-lg bg-gray-800",
class: props.class,
{props.children}
}
}
}
#[component]
pub fn Info(props: AlertProps) -> Element {
rsx! {
Base {
class: "text-blue-400 border-blue-800",
div {
span { class: "font-medium", "Info! " }
{props.children}
}
}
}
}
#[component]
pub fn Warning(props: AlertProps) -> Element {
rsx! {
Base {
class: "text-yellow-300 border-yellow-800",
div {
span { class: "font-medium", "Warning! " }
{props.children}
}
}
}
}
}
Could you elaborate on how struct based components would improve the existing solutions?
@ealmloff, your approach is indeed a good solution. However, if we want to prepare our struct in advance and use it elsewhere, I can imagine scenarios, especially complex ones, where this approach might be the better solution. It also allows us to develop the app with an OOP approach. There could even be cases where using generics would be beneficial.
This isn't something we want to add today but if it becomes useful maybe we'll reconsider it. It's hard to support lots of variants of app architectures while also shipping other important things, so not planned as of today.
but if it becomes useful maybe we'll reconsider it.
Can you explain in more detail? How will we understand that if it will become useful?
I think I can provide an example. Let's assume we want to initialize a component, but there are three different types of components that can be initialized based on the data. If we follow this implementation, it would be easier because MyStruct::Component{}
can return various components. We prepare the data beforehand, and the Component method examines this data to return different components accordingly. Currently, we must prepare the struct
and pass it as parameters to the component function. However, if we want to initialize the component by sending the struct
as a parameter, we must either manage all views in the same function or make comparisons in the parent rsx
macro, which decreases code readability. This implementation proposes writing Dioxus in a more abstracted and readable manner.
different types of components that can be initialized based on the data. If we follow this implementation, it would be easier because MyStruct::Component{} can return various components
Function components can also return various components, e.g. by dispatching with a match statement on the data that determines which one to initialize:
mod Alert {
#[derive(Props)]
struct AlertProps {
#[props(optional)] kind: String,
#[props(optional)] msg: String,
}
#[component]
fn Generic(props: AlertProps) -> Element {
match props.kind {
"error" => rsx! { Error { ..props } },
"warning" => rsx! { Warning { ..props } },
_ => unreachable!()
}
}
#[component]
fn Error(props: AlertProps) -> Element {
rsx! { p { "error {props.msg}" } }
}
#[component]
fn Warning(props: AlertProps) -> Element {
rsx! { p { "warning {props.msg}" } }
}
}
fn App() -> Element {
rsx! {
Alert::Generic { kind: "error", msg: "friends don't let friends use OOP" },
Alert::Error { msg: "react deprecated class components years ago" }
Alert::Warning { msg: "also I don't see how your idea resolves any of your complaints" }
}
}
You can imagine variations of this that use enum structs for the alert type/values if they have different ones, or some overly-complicated Rc<dyn Alertable>
technique to display arbitrary values in an alert.
Another idea:
impl Into<Element> for MyStruct {
fn into(self) -> Element {
rsx! {
...
}
}
}
Feature Request
I would like to propose a feature where we can use structs directly as components within the rsx! macro in Dioxus. This feature would let us group related components and define both the data and the rendering logic in the same struct.
Let’s look at this code:
These components are related to each other. With the Struct-Based Component feature, we could group these functions.
And usage in the rsx! macro:
This new approach would simplify how we organize and manage our code, making it cleaner and easier to maintain.