使用 React Router 的动态页面

2025-06-05

使用 React Router 的动态页面

嘿!

如果你曾经访问过一个拥有大量不同用户的网站,并且每个用户的内容各不相同,比如博客网站、社交媒体网站,甚至是dev.to,你可能已经注意到,特定用户的每个页面都有一条类似于 的路由,/username或者,如果你访问该用户在网站上的某篇文章,则有一条类似于 的路由/username/article。你甚至会注意到,虽然所有页面的结构都相似,但它们的内容却各不相同。

这就是所谓的动态页面和路由。

让我们看看如何在 React 中实现它。我们将使用星球大战 API 获取用户列表,并为所有用户生成单独的页面和路由。

请记住,本教程重点介绍使用 React Router 在 React 中实现动态路由。要在 Gatsby 或 Next.js 中实现相同的结果,步骤会有所不同,并且将依赖于它们的自定义路由 API。

开始

搞定这些,我们就开始吧。使用 创建一个新的 React 项目create-react-app。同时运行 安装 React Router yarn add/npm i react-router-dom。该react-router-dom模块将 React Router 的核心功能引入到基于浏览器的应用程序。

现在打开App.js并删除默认代码并添加以下导入语句。

import React, { useState, useEffect } from "react";
import { Link, BrowserRouter as Router, Route } from "react-router-dom";

const App = () => {
  return <></>;
};

export default App;
Enter fullscreen mode Exit fullscreen mode

在本教程中,我们将使用 React Hooks 来简化代码。在同一个App.js文件中,我们添加另一个名为 的组件HomePage。顾名思义,这将是我们的主页,我们将从这里调用星球大战 API 来获取人物数据。我们还将为该组件定义一个路由。

import React, { useState, useEffect } from "react";
import { Link, BrowserRouter as Router, Route } from "react-router-dom";

const HomePage = () => {
  return <>Home Page</>;
};

const App = () => {
  return (
    <>
      <Router>
        <Route exact path="/" component={HomePage} />
      </Router>
    </>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

太棒了!现在,如果你运行yarn start/npm start[localhost:3000/](http://localhost:3000/)在浏览器中访问,你应该会看到屏幕上显示“主页”。现在,让我们继续添加 API 调用。

import React, { useState, useEffect } from "react";
import { Link, BrowserRouter as Router, Route } from "react-router-dom";

const HomePage = () => {
  const [isLoading, setIsLoading] = useState(true);
  const [data, setData] = useState();

  useEffect(() => {
    fetch("https://swapi.dev/api/people/", {})
      .then((res) => res.json())
      .then((response) => {
        setData(response.results);
        setIsLoading(false);
      })
      .catch((error) => console.log(error));
  }, []);

  return (
    <>
      {!isLoading &&
        data.map((person, index) => {
          return <h5 key={index}>{person.name}</h5>;
        })}
    </>
  );
};

const App = () => {
  return (
    <>
      <Router>
        <Route exact path="/" component={HomePage} />
      </Router>
    </>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

现在有很多新代码。让我们分解一下我们写的内容。

我们有两个状态,isLoading一个是布尔值,它告诉我们是否从 API 接收到数据,data另一个是包含我们将从 API 调用接收的 JSON。

我们使用useEffect钩子在组件加载时获取数据HomePage。当我们从 API 获取数据时,我们将 的值设置isLoading为 false,并将data其设置为我们获取到的任何 JSON。

现在,如果您查看组件jsx内部HomePage,您会看到我们检查了值isLoading,如果它为假,我们就会进行映射data以呈现星球大战角色的名称。

如果您现在运行应用程序,您应该会看到名称一个接一个地弹出。

您可以在此处查看 Swapi 文档

制作动态组件

但是,我们不想要人员列表,我们还希望为每个人提供动态路线的单独页面!

因此,让我们创建另一个组件,PersonPage它将从 API 中获取数据以获取每个人的详细信息。

import React, { useState, useEffect } from "react";
import { Link, BrowserRouter as Router, Route } from "react-router-dom";

const PersonPage = () => {

  const [isLoading, setIsLoading] = useState(true);
  const [data, setData] = useState();

  useEffect(() => {
    fetch(`https://swapi.dev/api/people/${personId}`, {})
      .then((res) => res.json())
      .then((response) => {
        setData(response);
        setIsLoading(false);
        console.log(`https://swapi.dev/api/people/${personId}`)
      })
      .catch((error) => console.log(error));
  }, [personId]);

  return (
    <>
      {!isLoading && (
        <>
          <h1>Name: {data.name}</h1>
          <h2>Height: {data.height}</h2>
          <h2>Mass: {data.mass}</h2>
          <h2>Hair Color: {data.hair_color}</h2>
          <h2>Skin Color: {data.skin_color}</h2>
          <h2>Eye Color: {data.eye_color}</h2>
          <h2>Birth Year: {data.birth_year}</h2>
          <h2>Gender: {data.gender}</h2>
          <Link to="/">Back to homepage</Link>
        </>
      )}
    </>
  );
};

const HomePage = () => {
  const [isLoading, setIsLoading] = useState(true);
  const [data, setData] = useState();

  useEffect(() => {
    fetch("https://swapi.dev/api/people/", {})
      .then((res) => res.json())
      .then((response) => {
        setData(response.results);
        setIsLoading(false);
      })
      .catch((error) => console.log(error));
  }, []);

  return (
    <>
      {!isLoading &&
        data.map((person, index) => {
           return (
            <h5 key={index}>
              <Link to={`/person/${index + 1}`}>{person.name}'s Page</Link>
            </h5>
          );
        })}
    </>
  );
};

const App = () => {
  return (
    <>
      <Router>
        <Route exact path="/" component={HomePage} />
        <Route path="/person/:personId" component={PersonPage} />
      </Router>
    </>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

这又对我们的代码造成了很多改变。

我们定义了一个PersonPage组件,它通过从 API 获取数据来列出每个人的详细信息,方式与 相同HomePage。我们还为该组件定义了一个新的路由,即person/:personId。这与我们的常规路由略有不同。在这里,我们personId通过路由传递一个参数。这样,​​该路由上的单个组件就可以基于该参数动态变化。

HomePage也发生了变化,现在返回此动态路由的链接作为index路由参数。

如果你PersonPage仔细观察,就会发现虽然它的结构保持不变,但页面上的所有内容都依赖于personIdie,也就是说,该组件是完全动态的。但它PersonPage尚未访问此参数。这时,我们将使用 React Router 的一点魔力。

React Router 魔法

React Router 将两个 props 传递给其所有路由组件:

  • match道具
  • location道具

如果您想完整查看它们,可以在控制台中将它们注销。我们将使用matchprop 从组件访问路由参数PersonPage。该matchprop 有一个名为 的属性params,该属性将包含personId参数。让我们修改代码!

import React, { useState, useEffect } from "react";
import { Link, BrowserRouter as Router, Route } from "react-router-dom";
import "./App.css";

const PersonPage = ({ match }) => {
  const {
    params: { personId },
  } = match;
  const [isLoading, setIsLoading] = useState(true);
  const [data, setData] = useState();

  useEffect(() => {
    fetch(`https://swapi.dev/api/people/${personId}`, {})
      .then((res) => res.json())
      .then((response) => {
        setData(response);
        setIsLoading(false);
        console.log(`https://swapi.dev/api/people/${personId}`);
      })
      .catch((error) => console.log(error));
  }, [personId]);

  return (
    <>
      {!isLoading && (
        <>
          <h1>Name: {data.name}</h1>
          <h2>Height: {data.height}</h2>
          <h2>Mass: {data.mass}</h2>
          <h2>Hair Color: {data.hair_color}</h2>
          <h2>Skin Color: {data.skin_color}</h2>
          <h2>Eye Color: {data.eye_color}</h2>
          <h2>Birth Year: {data.birth_year}</h2>
          <h2>Gender: {data.gender}</h2>
          <Link to="/">Back to homepage</Link>
        </>
      )}
    </>
  );
};

const HomePage = () => {
  const [isLoading, setIsLoading] = useState(true);
  const [data, setData] = useState();

  useEffect(() => {
    fetch("https://swapi.dev/api/people/", {})
      .then((res) => res.json())
      .then((response) => {
        setData(response.results);
        setIsLoading(false);
      })
      .catch((error) => console.log(error));
  }, []);

  return (
    <>
      {!isLoading &&
        data.map((person, index) => {
          return (
            <h5 key={index}>
              <Link to={`/person/${index + 1}`}>{person.name}'s Page</Link>
            </h5>
          );
        })}
    </>
  );
};

const App = () => {
  return (
    <>
      <Router>
        <Route exact path="/" component={HomePage} />
        <Route path="/person/:personId" component={PersonPage} />
      </Router>
    </>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

就这样!

PersonPage现在personId通过 ES6 解构访问参数并将其用于 API 调用。运行你的 React 应用,你会看到HomePage它自身填充了链接,点击任何人的链接都会跳转到一个包含该人所有详细信息的动态页面。路由也将以 的形式动态呈现/person/{number}

结论

如果你想深入了解 React Router 并发现它可以做的所有酷事,请阅读官方文档,

React Router 文档

如有任何疑问,请联系我的 社交媒体 或 GitHub

文章来源:https://dev.to/gdsckiitdev/dynamic-pages-using-react-router-2pm
PREV
6分钟了解区块链
NEXT
Mint - 一种令人耳目一新的前端 Web 编程语言 Mint 是什么?