英文:
How do I use a dynamic URL that won't redirect to error page when the URL doesn't match any of the routes I've declared? - react-router-dom
问题 {#heading}
我创建了一个使用react-router-dom
的项目,我希望URL包含一个id。该id将用于从数据库中获取与该id对应的项目信息并在页面上呈现它。我已经成功地根据URL中的id实现了数据加载。
我的问题是,react-router-dom
似乎将带有id的URL解释为未知路由,并将其重定向到我的错误页面。
这是我的路由设置方式:
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
errorElement: <Error />,
children: [
{ path: "/", element: <Home /> },
{ path: "progress", element: <Progress /> },
{ path: "logworkout", element: <LogWorkout /> },
{
path: "programs",
element: <Programs />,
children: [
{ index: true, element: <Navigate to="myprograms" replace /> },
{ path: "discover", element: <DiscoverPrograms /> },
{
path: "myprograms",
element: <MyPrograms />,
children: [
{
path: ":programID",
element: <ProgramView />,
loader: programViewLoader,
},
],
},
],
},
{ path: "product", element: <Product /> },
{ path: "contact", element: <Contact /> },
],
},
]);
我基本上希望/programs/myprograms/:programID
能够呈现,而不是错误页面。我认为我很好地遵循了react-router-dom
文档中的教程,但我无法弄清楚我漏掉了什么。
编辑:仅/programs
中的<Programs/>
渲染了一个<Outlet>
import SideBar from "../../components/Programs/SideBar";
import { Outlet } from "react-router-dom";
const Programs = () => {
return (
<div className="grid overflow-hidden" style={{ gridTemplateColumns: "auto 1fr" }}>
<SideBar />
<div className="overflow-auto">
<Outlet />
</div>
</div>
);
};
export default Programs;
编辑:问题不在于动态路由,而在于我用于<ProgramView/>
的加载器函数。显然,我没有在加载器中返回值。
import Cookies from "js-cookie";
import { refreshToken } from "../../../../util/auth";
export const programViewLoader = ({ params }) => {
refreshToken()
.then((refresh) => {
return fetch("http://localhost:5000/program/load-program", {
method: "POST",
headers: {
"Content-type": "application/json",
Authorization: `Bearer ${Cookies.get("accessToken")}`,
},
body: JSON.stringify({
programID: params.programID,
}),
});
})
.then((response) => {
if (!response.ok) {
return response.json().then((data) => {
throw { error: data.error, status: response.status };
});
}
return response.json();
})
.then((data) => {
console.log(data.program);
return data.program;
})
.catch((error) => {
return { error: "加载程序时发生错误。" };
});
};
然而,console.log()
总是打印出某些内容,所以我不确定为什么它没有返回任何内容。
英文:
I am creating a project with react-router-dom where I want the URL to hold an id. The id will be used to fetch information about the item with that id from a database and render it on the page. I've already successfully implemented data loading based on the id in the url.
My issue is that react-router-dom seems to interpret the url with the id as an unknown route and redirects it to my error page.
This is how my routes are set up:
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
errorElement: <Error />,
children: [
{ path: "/", element: <Home /> },
{ path: "progress", element: <Progress /> },
{ path: "logworkout", element: <LogWorkout /> },
{
path: "programs",
element: <Programs />,
children: [
{ index: true, element: <Navigate to="myprograms" replace /> },
{ path: "discover", element: <DiscoverPrograms /> },
{
path: "myprograms",
element: <MyPrograms />,
children: [
{
path: ":programID",
element: <ProgramView />,
loader: programViewLoader,
},
],
},
],
},
{ path: "product", element: <Product /> },
{ path: "contact", element: <Contact /> },
],
},
]);
I basically want /programs/myprograms/:programID to render instead of the error page. I think I followed the tutorial on react-router-dom's documentation pretty well and I can't figure out what I'm missing.
Edit: Only <Programs/>
in /programs
renders an Outlet
import { Outlet } from "react-router-dom";
const Programs = () =\> {
return (
\<div className=\"grid overflow-hidden\" style={{gridTemplateColumns: \"auto 1fr\"}}\>
\<SideBar /\>
\<div className=\"overflow-auto\"\>
\<Outlet /\>
\</div\>
\</div\>
);
};
`export default Programs;
`
Edit: The issue isn't about the dynamic route but about the loader function I'm using for <ProgramView/>
. Apparently, I'm not returning a value in the loader.
import Cookies from "js-cookie";
import { refreshToken } from "../../../../util/auth";
`export const programViewLoader = ({ params }) => {
refreshToken()
.then((refresh) => {
return fetch("http://localhost:5000/program/load-program", {
method: "POST",
headers: {
"Content-type": "application/json",
Authorization: ``Bearer ${Cookies.get(&quot;accessToken&quot;)}``,
},
body: JSON.stringify({
programID: params.programID,
}),
});
})
.then((response) => {
if (!response.ok) {
return response.json().then((data) => {
throw { error: data.error, status: response.status };
});
}
return response.json();
})
.then((data) => {
console.log(data.program);
return data.program;
})
.catch((error) => {
return { error: "An error occurred while loading the program." };
});
};
`
However the console.log()
always prints something so I'm not sure how it isn't returning anything
答案1 {#1}
得分: 1
只有<Programs />
在"/programs"
中渲染Outlet
。
这只允许***直接*** 嵌套的路由渲染到父路由组件的Outlet
中,例如:
- 在
"/programs"
索引路由上的Navigate
- 在
"/programs/discover"
上的DiscoverPrograms
- 在
"/programs/myprograms"
上的MyPrograms
这些组件中的任何一个都需要再次渲染自己的Outlet
,以渲染可能的嵌套路由。
const MyPrograms = () => {
...
return (
...
<Outlet />
...
);
};
现在ProgramView
应该可以通过MyPrograms
的Outlet
通过Programs
的Outlet
在"/programs/myprograms/:programID"
上渲染。
programViewLoader
没有返回值。refreshToken
函数返回一个Promise链,但您需要实际从加载器函数返回Promise链的结果。
export const programViewLoader = ({ params }) => {
return refreshToken() // <-- 返回Promise链!
.then((refresh) => {
return fetch("http://localhost:5000/program/load-program", {
method: "POST",
headers: {
"Content-type": "application/json",
Authorization: `Bearer ${Cookies.get("accessToken")}`,
},
body: JSON.stringify({
programID: params.programID,
}),
});
})
.then((response) => {
if (!response.ok) {
return response.json().then((data) => {
throw { error: data.error, status: response.status };
});
}
return response.json();
})
.then((data) => {
console.log(data.program);
return data.program;
})
.catch((error) => {
return { error: "加载程序时发生错误。" };
});
};
英文:
> Only <Programs />
in "/programs"
renders an Outlet
This allows only the immediately nested routes to be rendered into the parent route component's Outlet
, e.g:
Navigate
on the"/programs"
index routeDiscoverPrograms
on"/programs/discover"
MyPrograms
on"/programs/myprograms"
Any of these components would need to, yet again, render another Outlet
of their own for any nested routes they may be rendering.
const MyPrograms = () => {
...
return (
...
<Outlet />
...
);
};
Now ProgramView
should be renderable via MyPrograms
' Outlet
via Programs
' Outlet
on "/programs/myprograms/:programID"
.
The programViewLoader
isn't returning a value. The refreshToken
function returns a Promise chain, but then you need to actually return the result of the Promise chain from the loader function.
export const programViewLoader = ({ params }) => {
return refreshToken() // <-- return Promise chain!
.then((refresh) => {
return fetch("http://localhost:5000/program/load-program", {
method: "POST",
headers: {
"Content-type": "application/json",
Authorization: `Bearer ${Cookies.get("accessToken")}`,
},
body: JSON.stringify({
programID: params.programID,
}),
});
})
.then((response) => {
if (!response.ok) {
return response.json().then((data) => {
throw { error: data.error, status: response.status };
});
}
return response.json();
})
.then((data) => {
console.log(data.program);
return data.program;
})
.catch((error) => {
return { error: "An error occurred while loading the program." };
});
};