英文:
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
问题
我创建了一个使用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("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." };
});
};
However the console.log() always prints something so I'm not sure how it isn't returning anything
答案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:
Navigateon the"/programs"index routeDiscoverProgramson"/programs/discover"MyProgramson"/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." };
});
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论