Fix issues with route matching on different sub-routes with same path (closes issue #229)

This commit is contained in:
Greg Johnston 2023-01-03 19:32:58 -05:00
parent ca679ec496
commit e2a5c2d78f
4 changed files with 31 additions and 11 deletions

View file

@ -1,4 +1,4 @@
use std::{cell::RefCell, rc::Rc};
use std::{cell::Cell, rc::Rc};
use crate::use_route;
use leptos::*;
@ -8,21 +8,18 @@ use leptos::*;
#[component]
pub fn Outlet(cx: Scope) -> impl IntoView {
let route = use_route(cx);
let is_showing = Rc::new(RefCell::new(None));
let is_showing = Rc::new(Cell::new(None));
let (outlet, set_outlet) = create_signal(cx, None);
create_effect(cx, move |_| {
let is_showing_val = { is_showing.borrow().clone() };
match (route.child(), &is_showing_val) {
match (route.child(), &is_showing.get()) {
(None, _) => {
set_outlet.set(None);
}
(Some(child), Some(_))
if Some(child.original_path().to_string()) == is_showing_val =>
{
(Some(child), Some(is_showing_val)) if child.id() == *is_showing_val => {
// do nothing: we don't need to rerender the component, because it's the same
}
(Some(child), _) => {
*is_showing.borrow_mut() = Some(child.original_path().to_string());
is_showing.set(Some(child.id()));
provide_context(child.cx(), child.clone());
set_outlet.set(Some(child.outlet().into_view(cx)))
}

View file

@ -1,4 +1,4 @@
use std::{borrow::Cow, rc::Rc};
use std::{borrow::Cow, cell::Cell, rc::Rc};
use leptos::*;
@ -7,6 +7,10 @@ use crate::{
ParamsMap, RouterContext,
};
thread_local! {
static ROUTE_ID: Cell<usize> = Cell::new(0);
}
/// Describes a portion of the nested layout of the app, specifying the route it should match,
/// the element it should display, and data that should be loaded alongside the route.
#[component(transparent)]
@ -43,7 +47,13 @@ where
.collect::<Vec<_>>()
})
.unwrap_or_default();
let id = ROUTE_ID.with(|id| {
let next = id.get() + 1;
id.set(next);
next
});
RouteDefinition {
id,
path: path.to_string(),
children,
view: Rc::new(move |cx| view(cx).into_view(cx)),
@ -73,7 +83,9 @@ impl RouteContext {
let base = base.path();
let RouteMatch { path_match, route } = matcher()?;
let PathMatch { path, .. } = path_match;
let RouteDefinition { view: element, .. } = route.key;
let RouteDefinition {
view: element, id, ..
} = route.key;
let params = create_memo(cx, move |_| {
matcher()
.map(|matched| matched.path_match.params)
@ -83,6 +95,7 @@ impl RouteContext {
Some(Self {
inner: Rc::new(RouteContextInner {
cx,
id,
base_path: base.to_string(),
child: Box::new(child),
path,
@ -98,6 +111,10 @@ impl RouteContext {
self.inner.cx
}
pub(crate) fn id(&self) -> usize {
self.inner.id
}
/// Returns the URL path of the current route,
/// including param values in their places.
///
@ -125,6 +142,7 @@ impl RouteContext {
Self {
inner: Rc::new(RouteContextInner {
cx,
id: 0,
base_path: path.to_string(),
child: Box::new(|| None),
path: path.to_string(),
@ -154,6 +172,7 @@ impl RouteContext {
pub(crate) struct RouteContextInner {
cx: Scope,
base_path: String,
pub(crate) id: usize,
pub(crate) child: Box<dyn Fn() -> Option<RouteContext>>,
pub(crate) path: String,
pub(crate) original_path: String,

View file

@ -86,7 +86,8 @@ pub fn Routes(
match (prev_routes, prev_match) {
(Some(prev), Some(prev_match))
if next_match.route.key == prev_match.route.key =>
if next_match.route.key == prev_match.route.key
&& next_match.route.id == prev_match.route.id =>
{
let prev_one = { prev.borrow()[i].clone() };
if i >= next.borrow().len() {
@ -212,6 +213,7 @@ struct RouterState {
#[derive(Debug, Clone, PartialEq)]
pub struct RouteData {
pub id: usize,
pub key: RouteDefinition,
pub pattern: String,
pub original_path: String,
@ -286,6 +288,7 @@ fn create_routes(route_def: &RouteDefinition, base: &str) -> Vec<RouteData> {
};
acc.push(RouteData {
key: route_def.clone(),
id: route_def.id,
matcher: Matcher::new_with_partial(&pattern, !is_leaf),
pattern,
original_path: original_path.to_string(),

View file

@ -5,6 +5,7 @@ use leptos::*;
#[derive(Clone)]
pub struct RouteDefinition {
pub id: usize,
pub path: String,
pub children: Vec<RouteDefinition>,
pub view: Rc<dyn Fn(Scope) -> View>,