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

huangapple go评论68阅读模式
英文:

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: &quot;/&quot;,
    element: &lt;Root /&gt;,
    errorElement: &lt;Error /&gt;,
    children: [
      { path: &quot;/&quot;, element: &lt;Home /&gt; },
      { path: &quot;progress&quot;, element: &lt;Progress /&gt; },
      { path: &quot;logworkout&quot;, element: &lt;LogWorkout /&gt; },
      {
        path: &quot;programs&quot;,
        element: &lt;Programs /&gt;,
        children: [
          { index: true, element: &lt;Navigate to=&quot;myprograms&quot; replace /&gt; },
          { path: &quot;discover&quot;, element: &lt;DiscoverPrograms /&gt; },
          {
            path: &quot;myprograms&quot;,
            element: &lt;MyPrograms /&gt;,
            children: [
              {
                path: &quot;:programID&quot;,
                element: &lt;ProgramView /&gt;,
                loader: programViewLoader,
              },
            ],
          },
        ],
      },
      { path: &quot;product&quot;, element: &lt;Product /&gt; },
      { path: &quot;contact&quot;, element: &lt;Contact /&gt; },
    ],
  },
]);

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 &lt;Programs/&gt; in /programs renders an Outlet

import { Outlet } from &quot;react-router-dom&quot;;

const Programs = () =&gt; {
  return (
    &lt;div className=&quot;grid overflow-hidden&quot; style={{gridTemplateColumns: &quot;auto 1fr&quot;}}&gt;
      &lt;SideBar /&gt;
      &lt;div className=&quot;overflow-auto&quot;&gt;
        &lt;Outlet /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default Programs;

Edit: The issue isn't about the dynamic route but about the loader function I'm using for &lt;ProgramView/&gt;. Apparently, I'm not returning a value in the loader.

import Cookies from &quot;js-cookie&quot;;
import { refreshToken } from &quot;../../../../util/auth&quot;;

export const programViewLoader = ({ params }) =&gt; {
  refreshToken()
    .then((refresh) =&gt; {
      return fetch(&quot;http://localhost:5000/program/load-program&quot;, {
        method: &quot;POST&quot;,
        headers: {
          &quot;Content-type&quot;: &quot;application/json&quot;,
          Authorization: `Bearer ${Cookies.get(&quot;accessToken&quot;)}`,
        },
        body: JSON.stringify({
          programID: params.programID,
        }),
      });
    })
    .then((response) =&gt; {
      if (!response.ok) {
        return response.json().then((data) =&gt; {
          throw { error: data.error, status: response.status };
        });
      }
      return response.json();
    })
    .then((data) =&gt; {
      console.log(data.program);
      return data.program;
    })
    .catch((error) =&gt; {
      return { error: &quot;An error occurred while loading the program.&quot; };
    });
};

However the console.log() always prints something so I'm not sure how it isn't returning anything

答案1

得分: 1

只有&lt;Programs /&gt;&quot;/programs&quot;中渲染Outlet

这只允许直接嵌套的路由渲染到父路由组件的Outlet中,例如:

  • &quot;/programs&quot;索引路由上的Navigate
  • &quot;/programs/discover&quot;上的DiscoverPrograms
  • &quot;/programs/myprograms&quot;上的MyPrograms

这些组件中的任何一个都需要再次渲染自己的Outlet,以渲染可能的嵌套路由。

const MyPrograms = () =&gt; {
  ...

  return (
    ...

    &lt;Outlet /&gt;

    ...
  );
};

现在ProgramView应该可以通过MyProgramsOutlet通过ProgramsOutlet&quot;/programs/myprograms/:programID&quot;上渲染。

programViewLoader没有返回值。refreshToken函数返回一个Promise链,但您需要实际从加载器函数返回Promise链的结果。

export const programViewLoader = ({ params }) =&gt; {
  return refreshToken() // &lt;-- 返回Promise链!
    .then((refresh) =&gt; {
      return fetch(&quot;http://localhost:5000/program/load-program&quot;, {
        method: &quot;POST&quot;,
        headers: {
          &quot;Content-type&quot;: &quot;application/json&quot;,
          Authorization: `Bearer ${Cookies.get(&quot;accessToken&quot;)}`,
        },
        body: JSON.stringify({
          programID: params.programID,
        }),
      });
    })
    .then((response) =&gt; {
      if (!response.ok) {
        return response.json().then((data) =&gt; {
          throw { error: data.error, status: response.status };
        });
      }
      return response.json();
    })
    .then((data) =&gt; {
      console.log(data.program);
      return data.program;
    })
    .catch((error) =&gt; {
      return { error: &quot;加载程序时发生错误&quot; };
    });
};
英文:

> Only &lt;Programs /&gt; in &quot;/programs&quot; 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 &quot;/programs&quot; index route
  • DiscoverPrograms on &quot;/programs/discover&quot;
  • MyPrograms on &quot;/programs/myprograms&quot;

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 = () =&gt; {
  ...

  return (
    ...

    &lt;Outlet /&gt;

    ...
  );
};

Now ProgramView should be renderable via MyPrograms' Outlet via Programs' Outlet on &quot;/programs/myprograms/:programID&quot;.

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 }) =&gt; {
  return refreshToken() // &lt;-- return Promise chain!
    .then((refresh) =&gt; {
      return fetch(&quot;http://localhost:5000/program/load-program&quot;, {
        method: &quot;POST&quot;,
        headers: {
          &quot;Content-type&quot;: &quot;application/json&quot;,
          Authorization: `Bearer ${Cookies.get(&quot;accessToken&quot;)}`,
        },
        body: JSON.stringify({
          programID: params.programID,
        }),
      });
    })
    .then((response) =&gt; {
      if (!response.ok) {
        return response.json().then((data) =&gt; {
          throw { error: data.error, status: response.status };
        });
      }
      return response.json();
    })
    .then((data) =&gt; {
      console.log(data.program);
      return data.program;
    })
    .catch((error) =&gt; {
      return { error: &quot;An error occurred while loading the program.&quot; };
    });
};

huangapple
  • 本文由 发表于 2023年8月10日 14:58:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/76873281.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定