router icon indicating copy to clipboard operation
router copied to clipboard

Nested route match the first path with component

Open kuchta opened this issue 2 years ago • 6 comments

Describe the bug

I have a router like this

export const router = createReactRouter({
	routeConfig: createRouteConfig().createChildren(createRoute => [
		createRoute({
			path: '/',
			component: Home,
		}),
		createRoute({
			path: 'collections',
			// component: Collections,
		}).createChildren(createRoute => [
			createRoute({
				path: ':collectionId',
				component: Collection,
			}).createChildren(createRoute => [
				createRoute({
					path: 'images'
				}).createChildren(createRoute => [
					createRoute({
						path: ':imageId',
						component: Image,
					})
				])
			])
		]),
	])
});

And it only renders the first component in the path. If I go to /collections/new-collection/images/new-image it renders the Collection component, if I uncomment Collections component it renders Collections component.

Thank you for any help 🙏🏻

Your Example Website or App

https://stackblitz.com/edit/vitejs-vite-rs1z4u?file=src/App.tsx

Steps to Reproduce the Bug or Issue

...

Expected behavior

Match the longest matching path.

Screenshots or Videos

No response

Platform

  • OS: macOS
  • Browser: Brave
  • Version: 1.45.131 (Chromium: 107.0.5304.110)

Additional context

No response

kuchta avatar Nov 24 '22 14:11 kuchta

Try change path: 'collections', // component: Collections,

path: 'collections', // component: Outlet,

XpamAmAdEuS avatar Nov 24 '22 17:11 XpamAmAdEuS

It then passes through collections as if it's commented out, but otherwise exhibit the same behavior that router matches the "shortest" matching path, not the longest, which IMHO it should...

kuchta avatar Nov 24 '22 18:11 kuchta

You need in add <Outlet /> in the components Collections and Collection. That is where the nested routes will render.

Here is a modified stackblitz that works: https://stackblitz.com/edit/vitejs-vite-bkplun?file=src/App.tsx

wizzymore avatar Nov 25 '22 14:11 wizzymore

Thank you @wizzymore and @XpamAmAdEuS. I've got it :). So there is no way how to render just the longest matching route (like exact match in React Router) then to acculumate all the stuff along the way?

kuchta avatar Nov 25 '22 16:11 kuchta

You can create just one route and put it above the nested ones, that should work.

EDIT: To be more specific, create one route with the path: '/collections/new-collection/images/new-image' if you want to have only one "catch"

wizzymore avatar Nov 25 '22 18:11 wizzymore

Unfortunatelly I don't want only one "catch-all" route. I want one route for viewing all image collections (/collections), another one for viewing all images in particular collection (/collections/:collectionId or /collections/:collectionId/images) and another one for viewing just one image from particular collection (/collections/:collectionId/images/:imageId).

I was able to achieve that by using:

export const router = createReactRouter({
	routeConfig: createRouteConfig().createChildren(createRoute => [
		createRoute({
			path: '/',
			component: Home
		}),
		createRoute({
			path: 'collections/:collectionId/images/:imageId',
			component: Image
		}),
		createRoute({
			path: 'collections/:collectionId',
			component: Collection
		}),
		createRoute({
			path: 'collections',
			component: Collections
		}),
	])
});

I still think this a bug, since this should probably have the same semantics as the one using the child nodes...

Anyway, thank you @wizzymore for pointing me in the right direction to circumvent the issue 🙏🏻

kuchta avatar Nov 26 '22 03:11 kuchta

To tie up loose ends here, I believe this is what you are looking for:

const rootRoute = createRouteConfig({})

const indexRoute = rootRoute.createRoute({
  path: '/',
  component: Home,
})

const collectionsRoute = rootRoute.createRoute({
  path: 'collections',
  component: Collections,
})

const collectionIndexRoute = collectionsRoutes.createRoute({
  path: '/',
  component: CollectionIndex,
})

const collectionRoute = collectionsRoutes.createRoute({
  path: ':collectionId',
  component: Collection,
})

const imagesRoute = collectionRoute.createRoute({
  path: 'images',
  component: Image,
})

const imageRoute = collectionRoute.createRoute({
  path: ':imageId',
  component: Image,
})

const routes = createReactRouter({
  routes: rootRoute.addChildren([
    indexRoute,
    collectionsRoute.addChildren([
      collectionIndexRoute,
      collectionRoute.addChildren([imagesRoute.addChilren([imageRoute])]),
    ]),
  ]),
})

tannerlinsley avatar Dec 08 '22 00:12 tannerlinsley