Multiple routes with the same component not possible Enum based router
Problem
Perhaps, this is a limitation of my understanding of enums in Rust - if this is the case then this might be a simple docsite update, otherwise, when using the same "page" component across multiple routes, due to it being an enum, Rust is not a big of this. There might be use cases where the same component is used on multiple routes, perhaps with a variation.
Related:
- #922
You can have a different variant name and component name with this syntax #[route("path", ComponentName)]. That is currently only documented in the rust docs, but we could also document that syntax in the router reference in the guide
I have a related issue of building a router with language prefixes such as
/en/blog/post/1
/es/blog/post/1
/de/blog/post/1
One workaround would be to wrap it with #[nest("/lang/:lang")] but that would not provide any restrictions to what lang can be.
And using component renaming is a bit of a no-go if there is no way to pass the static language name to the component.
I worked around this problem with following route definition using the undocumented #[child] attribute and using a layout wrapper to store the language into context
pub enum BaseRoute {
#[layout(LanguageWrapper)]
#[child("/en")]
RouteEnglish { child: LocalizedRoute },
#[child("/fi")]
RouteFinnish { child: LocalizedRoute },
#[child("/ja")]
RouteJapanese { child: LocalizedRoute },
#[end_layout]
}
pub enum LocalizedRoute {
#[route("/recipe/:id")]
RecipeView { id: String },
}
#[component]
fn LanguageWrapper() -> Element {
// extract language from route
let route = use_route::<BaseRoute>().to_string();
let lang = route.split('/').nth(1).unwrap_or("en");
let language = lang.parse::<Language>().unwrap_or(Language::English);
// set language to context
use_context_provider(|| Signal::new(language.clone()));
rsx! {
Outlet::<BaseRoute> {}
}
}
With some further tweaking I was able to optimize it down to this, which works well for me.
#[derive(Clone, Debug, PartialEq, Routable)]
pub enum BaseRoute {
#[nest("/:lang")]
#[layout(LanguageWrapper)]
#[child("")]
RouteForLocalized { child: LocalizedRoute, lang: Language },
#[end_layout]
#[end_nest]
#[route("/")]
Home {},
#[route("/:..route")]
PageNotFound { route: Vec<String> },
}
// Routes that are localized to a specific language.
#[derive(Clone, Debug, PartialEq, Routable)]
pub enum LocalizedRoute {
#[route("/recipe/:id")]
RecipeView { id: String },
}
#[component]
fn LanguageWrapper(lang: Language) -> Element {
// set language to context
use_context_provider(|| Signal::new(lang.clone()));
rsx! {
Outlet::<BaseRoute> {}
}
}
You just need to get everything exactly right through experimentation, because the macro error messages are typically not helpful at all.
Also, if passing language directly to the components, it simplifies to
#[derive(Clone, Debug, PartialEq, Routable)]
pub enum BaseRoute {
#[nest("/:lang")]
#[route("/recipe/:id")]
RecipeView { lang: Language, id: String },
#[end_nest]
#[route("/")]
Home {},
#[route("/:..route")]
PageNotFound { route: Vec<String> },
}