'how to create deep paths in a tree structure in react-router v6
Here is a codesandbox that illustrates the problem:
If I have a tree of objects like this:
const tasks: Task[] = [
{
id: 1,
name: "one",
children: [
{
id: 3,
name: "three",
children: [{ id: 5, name: "five", children: [] }]
}
]
},
{
id: 2,
name: "two",
children: [
{ id: 4, name: "four", children: [{ id: 6, name: "six", children: [] }] }
]
}
];
I would like to be able to link to a url like this:
/tasks/2/children/4/children/6
.
So far I can only get it to link to tasks/2/children/6
with the following route structure:
let routes: RouteObject[] = [
{
path: "tasks",
element: <Layout />,
children: [
{
path: ":id",
element: <TaskPage />,
children: [
{ path: "children/:id", element: <TaskPage />, index: true }
]
},
{
index: true,
element: <TaskTreePage />
}
]
},
{
index: true,
element: <Navigate to="tasks" />
}
];
let element = useRoutes(routes);
I would need to continually add a nested route every time I went a new level deep.
Is there a way I could dynamically construct a deeply nested route path that works for any depth?
Solution 1:[1]
When a route is an index route it shouldn't also use a path
prop as it's understood the path is that of the wrapping parent layout route.
The TaskPage
component is what should be dynamic, not the routes config. The routes config should be the entry point to the "root" route rendering the "root" TaskPage
component on "/tasks/:id/*"
. The TaskPage
component is now effectively recursively rendering another instance of itself in a new nested relative route <Route path="children/:id/*" element={<TaskPage />} />
that it renders.
Code example:
App
const routes: RouteObject[] = [
{
path: "/tasks",
element: <Layout />,
children: [
{
path: ":id/*",
element: <TaskPage />
},
{
index: true,
element: <TaskTreePage />
}
]
},
{
index: true,
element: <Navigate to="/tasks" />
}
];
export default function App() {
const element = useRoutes(routes);
return element;
}
TaskPage
This is where the "magic" happens. It renders a new Routes
component with two routes, an index route to render the links for the current depth and a route to render the nested children.
function TaskPage() {
const { id } = useParams();
const task = findTask(id as string, tasks);
if (!task) {
return <h2>Not Found!</h2>;
}
return (
<Routes>
<Route
index
element={
<div>
<h2>task of {task.name}</h2>
<div>
<Link to="../..">Back</Link>
<div>
<h2>Children</h2>
{task.children.map((child: Task) => (
<Link key={child.id} to={`children/${child.id}`}>
{child.name}
</Link>
))}
</div>
</div>
</div>
}
/>
<Route path="children/:id/*" element={<TaskPage />} />
</Routes>
);
}
Note the trailing wildcard "*"
matcher to allow matching of nested routes.
Note also the back link is now "../.."
since each level of nesting pushes two path segments, so to go "back" two path segments need to be removed.
const tasks: Task[] = [
{
id: 1,
name: "one",
children: [
{
id: 3,
name: "three",
children: [
{
id: 5,
name: "five",
children: [{ id: 7, name: "seven", children: [] }]
}
]
}
]
},
{
id: 2,
name: "two",
children: [
{ id: 4, name: "four", children: [{ id: 6, name: "six", children: [] }] }
]
}
];
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Drew Reese |