第 6 章 实现请求和 React Suspense

第 6 章 实现请求和 React Suspense

Flying
2021-02-26 / 0 评论 / 240 阅读 / 正在检测是否收录...

在前面的章节中,我们学习了如何使用 React Context 作为手动传递 props 的替代方案。我们了解了context provider、consumer 以及如何将 Hooks 用作 context Consumer。接下来,我们了解了控制反转作为 context 的替代方案。最后,我们在博客应用程序中使用 context 实现了主题和全局状态。

在本章中,我们将设置一个简单的后端服务器,它将使用 json-server 工具从 JavaScript 对象表示法JSON) 文件生成服务。然后,我们将通过将 Effect Hook 与 State Hook 结合使用来实现请求资源。接下来,我们将使用 axiosreact-request-hook 库做同样的事情。最后,我们将看看如何使用 React.memo 避免不必要的重新渲染,以及通过使用 React Suspense 延迟加载组件。

本章将介绍以下主题:

  • 使用 Hook 请求资源
  • 使用 React.memo 避免不必要的重新渲染
  • 使用 React Suspense 实现延迟加载

技术要求

应该已经安装了相当新版本的 Node.js(vl1.l2.0 或更高版本)。还需要安装 Node.js 的 npm 包管理器。
本章的代码可以在 GitHub 存储库中找到:https://github.com/PacktPublishing/Learn-React-Hooks/tree/master/Chapter06

观看以下视频,了解代码的实际应用:

http://bit.ly/2Mm9yoC

请注意,强烈建议您自己编写代码。不要简单地运行已提供的代码示例。重要的是你自己编写代码,以便你能够正确学习和理解。但是,如果遇到任何问题,始终可以参考代码示例。

现在,让我们从本章开始。

使用 Hook 请求资源

在本节中,我们将学习如何使用 Hook 从服务器请求资源。首先,我们将仅使用 JavaScript fetch 函数和 useEffect/useState Hook 来实现请求。然后,我们将学习如何组合使用 axiosreact-request-hook 库来请求资源。

设置虚拟服务器

在实现请求之前,我们需要创建一个后端服务器。由于我们目前专注于用户界面,因此我们将设置一个虚拟服务器来测试请求。我们将使用 json-server 工具从 JSON 文件创建完整的表述性状态传输REST) API。

创建 db.json 文件

为了能够使用 json 服务器 工具,首先我们需要创建一个 db.json 文件,该文件将包含服务器的完整数据库。json-server 工具将允许您执行以下操作:

  • GET 请求,用于从文件中获取数据
  • POST 请求,将新数据插入文件
  • PUTPATCH 请求,修改现有数据
  • DELETE 请求,删除数据

对于所有修改操作(POSTPUTPATCHDELETE ),更新后的文件将由工具自动保存。

我们可以对帖子使用现有的结构,将其定义为 Post Reducer 的默认状态。但是,我们需要确保提供一个 id 值,以便稍后可以查询数据库:

[
  { "id": "react-hooks", "title": "React Hooks", "content": "The greatest thing since sliced bread!", "author": "Daniel Bugl" },
  { "id": "react-fragments", "title": "Using React Fragments", "content": "Keeping the DOM tree clean!", "author": "Daniel Bugl" }
]

至于用户,我们需要想出一种存储用户名和密码的方法。为简单起见,我们仅以纯文本形式存储密码(不要在生产环境中执行此操作!在这里,我们还需要提供一个 id 值:

[
  { "id": 1, "username": "Daniel Bugl", "password": "supersecure42" }
]

此外,我们将在数据库中存储主题。为了验证从我们的数据库中提取主题是否正常工作,我们现在将定义第三个主题。与往常一样,每个主题都需要一个 id 值:

[
  { "id": 1, "primaryColor": "deepskyblue", "secondaryColor": "coral" },
  { "id": 2, "primaryColor": "orchid", "secondaryColor": "mediumseagreen" },
  { "id": 3, "primaryColor": "darkslategray", "secondaryColor": "slategray" }
]

现在,剩下要做的就是将这三个数组合并到一个 JSON 对象中,方法是将 posts 数组存储在 posts 键下,将 users 数组存储在 users 键下,将主题数组存储在themes 键下。

让我们开始创建用作后端服务器数据库的 JSON 文件:

  1. 在应用程序文件夹的根目录中创建一个新的 server/ 目录。
  2. 创建包含以下内容的 server/db.json 文件。我们可以从 Reducer Hook 中使用现有状态。但是,由于这是一个数据库,我们需要给每个元素一个 id 值:
{

  "posts": [
    { "id": "react-hooks", "title": "React Hooks", "content": "The greatest thing since sliced bread!", "author": "Daniel Bugl" },
    { "id": "react-fragments", "title": "Using React Fragments", "content": "Keeping the DOM tree clean!", "author": "Daniel Bugl" }
  ],

  "users": [
    { "id": 1, "username": "Daniel Bugl", "password": "supersecure42"}
  ],

  "themes": [
    { "id": 1, "primaryColor": "deepskyblue", "secondaryColor": "coral" },
    { "id": 2, "primaryColor": "orchid", "secondaryColor": "mediumseagreen" },
    { "id": 3, "primaryColor": "darkslategray", "secondaryColor": "slategray" }
  ]
}

对于 json-server 工具,我们只需要一个 JSON 文件作为数据库,该工具将为我们创建一个完整的 REST API。

安装 json-server 工具

现在,我们将安装 json-server 工具并使用它启动我们的后端服务器:

  1. 首先,我们将通过 npm 安装 json-server 工具:
npm install --save json-server
  1. 现在,我们可以通过调用以下命令来启动后端服务器:
npx json-server --watch server/db.json

npx 命令执行项目中本地安装的命令。我们需要在这里使用 npx,因为我们没有全局安装 json-server 工具(通过 npm install -g json-server)。

我们执行了 json-server 工具,并让它监视之前创建的 server/db.json 文件。--watch 标志表示它将监听对文件的更改,并自动刷新。

现在,我们可以去 http://localhost:3000/posts/react-hooks 为了查看我们的帖子对象:

json-server-check.jpg
我们简单的 json-server 工作了并提供一个帖子的数据!

如我们所见,该工具从数据库 JSON 文件为我们创建了一个完整的 REST API!

配置 package.json

接下来,我们需要调整我们的 package.json 文件,以便启动服务器以及我们的客户端(通过运行 webpack-dev-server )。
让我们开始调整 package.json 文件:

  1. 首先,我们创建一个名为 start:server 的新包脚本,方法是将其插入 package.json 文件的脚本部分。我们还确保更改端口,使其不会在与客户端相同的端口上运行:
"scripts": {
  "start:server": "npx json-server --watch server/db.json --port 4000",
  "start": "react-scripts start",
  1. 然后,我们将启动脚本重命名为 start:client
"scripts": {
  "start:server": "npx json-server --watch server/db.json", 
  "start:client": "react-scripts start",
  1. 接下来,我们安装一个名为 concurrently 的工具,它让我们同时启动服务器和客户端:
npm install --save concurrently
  1. 现在,我们可以通过使用并发命令定义一个新的启动脚本,然后将服务器和客户端命令作为参数传递给它:
"scripts": {
  "start": "npx concurrently \"npm run start:server\" \"npm run start:client\"

现在,运行 npm start 将运行客户端以及后端服务器。

配置代理

最后,我们必须定义一个代理,以确保我们可以从与客户端相同的 统一资源定位器 (URL) 请求我们的 API。这是必需的,否则,我们将不得不处理跨站点请求,这些请求处理起来有点复杂。我们将定义一个代理,该代理将来自 http://localhost:3000/api/ 请求转发到 http://localhost:4000/

现在,让我们配置代理:

  1. 首先,我们必须安装 http-proxy-middleware 包:
npm install --save http-proxy-middleware
  1. 然后,我们创建一个新的 src/setupProxy.js 文件,其中包含以下内容:
const proxy = require('http-proxy-middleware')
module.exports = function (app) {
  app.use(proxy('/api', {
  1. 接下来,我们必须定义代理的目标,它将是后端服务器,运行在 http://localhost:4000

    target: 'http://localhost:4000',
  2. 最后,我们必须定义一个路径重写规则,该规则将在请求转发到我们的服务器之前删除 /api 前缀:
      pathRewrite: { '^/api': '' }
    }))
}

前面的代理配置会将 /api 链接到我们的后端服务器;因此,我们现在可以通过以下命令启动服务器和客户端:

npm start

然后,我们可以通过打开来访问 API http://localhost:3000/api/posts/react-hooks 了!

定义路由

默认情况下,json-server工具定义以下路由:https://github.com/ typicode/json-server#routes

我们还可以通过创建一个 routes.json 文件来定义我们自己的路由,我们可以在其中将现有路由重写为其他路由:https://github.com/typicode/json-server#add-custom-routes

对于我们的博客应用程序,我们将定义一个自定义路由:/login/:username/:password。我们要把它链接到 /users?username=:username&password=:password 参数,以便找到具有给定用户名和密码组合的用户。

我们现在将为我们的应用程序定义自定义登录路由:

  1. 创建一个包含以下内容的新 server/routes.json 文件:
{
  "/login/:username/:password": "/users?username=:username&password=:password"
}
  1. 然后,调整 package.json 文件中的 start:server 脚本,并添加 –-routes 选项,如下所示:
"start:server": "npx json-server --watch server/db.json --port 4000 --routes server/routes.json",

现在,我们的服务器将为我们的自定义登录路由提供服务,我们将在本章后面使用它!我们可以通过在浏览器中打开以下 URL 来尝试登录:http://localhost:3000/api/login/Daniel%20Bugl/supersecure42,这将返回一个用户对象,登录成功了!

我们可以看到用户对象在浏览器中以文本形式返回:

custom-route.jpg
直接在浏览器中访问我们的自定义路由

如我们所见,访问我们的自定义路由有效!我们现在可以使用它来登录用户。

示例代码

示例代码可以在 Chapter06/chapter6_1 文件夹中找到。

只需运行 npm install 即可安装所有依赖项,运行 npm start 启动应用程序,然后在您的浏览器中访问 http://localhost:3000(如果它没有自动打开)。

使用 Effect 和 State/Reducer Hook 实现请求

在使用库使用 Hook 实现请求之前,我们将手动实现它们,使用 Effect Hook 触发请求,并使用 State/Reducer Hook 来存储结果。

使用 Effect 和 State Hook 的请求

首先,我们将从服务器请求主题,而不是对主题列表进行硬编码。

让我们使用 Effect Hook 和 State Hook 实现请求主题:

  1. src/ChangeTheme.js 文件中,调整 React import 语句以导入 useEffectuseState Hook:
import React, { useEffect, usestate } from 'react'
  1. 删除 THEMES 常量,即以下所有代码:
const THEMES = [
  { primaryColor: 'deepskyblue', secondaryColor: 'coral' },
  { primaryColor: 'orchid', secondaryColor: 'mediumseagreen' }
]
  1. ChangeTheme 组件中,定义一个新的 useState Hook 以存储主题:
export default function ChangeTheme({ theme, setTheme }) {
  const [themes, setThemes] = usestate([])
  1. 然后定义一个 useEffect Hook,我们将在其中发出请求:
useEffect(() => {
  1. 在这个 Hook 中,我们使用 fetch 来请求资源;本例中我们请求 /api/themes
fetch('/api/themes')
  1. Fetch 使用了 Promise API;因此,我们可以使用 .then() 来处理结果。首先,我们必须将结果解析为 JSON:
.then(result => result.json())
  1. 最后,我们使用请求中的 themes 数组调用 setThemes
.then(themes => setThemes(themes))
我们还可以将上述函数缩短为 .then(setThemes),因为我们只是从 .then() 传递主题参数。
  1. 目前,这个 Effect Hook 应该只在组件挂载时触发,因此我们将一个空数组作为第二个参数传递给 useEffect。这可确保 Effect Hook 没有依赖项,因此仅在组件挂载时触发:
}, [])
  1. 现在,剩下要做的就是将 THEMES 常量替换为 Hook 中的 themes 值:
{themes.map(t =>

正如我们所看到的,现在有三个主题可用,它们都通过我们的服务器从我们的数据库加载:

themes-loaded.jpg
使用 Hook 从我们的服务器加载三个主题!

我们的主题现在从后端服务器加载,我们可以继续通过 Hook 请求帖子。

使用 Effect 和 Reducer Hook 请求

我们现在将使用后端服务器来请求 posts 数组,而不是将其硬编码为 postsReducer 的默认值。

让我们使用 Effect Hook 和 Reducer Hook 实现请求帖子:

  1. src/App.js删除 defaultPosts 常量定义,该定义是以下所有代码:
const defaultPosts = [
  { title: 'React Hooks', content: 'The greatest thing since sliced bread!', author: 'Daniel Bugl' },
  { title: 'Using React Fragments', content: 'Keeping the DOM tree clean!', author: 'Daniel Bugl' }
]
  1. useReducer 函数中的 defaultPosts 常量替换为空数组:
const [state, dispatch] = useReducer(appReducer, {
  user: '',
  posts: []
})
  1. src/reducers.js 中,在 postsReducer 函数中定义一个名为 FETCH_POSTS 的 action 新类型。此 action 类型将用新的帖子数组替换当前状态:
function postsReducer (state, action) {
  switch (action.type) {
    case 'FETCH_POsTs':
      return action.posts
  1. src/App.js 中,定义一个新的 useEffect Hook,它位于当前 Hook 之前:
useEffect(() => {
  1. 在这个 Hook 中,我们再次使用 fetch 来请求资源;本例中我们请求 /api/posts
fetch('/api/posts')
  .then(result => result.json())
  1. 最后,我们使用请求中的 posts 数组调度一个 FETCH_POSTS action:
.then(posts => dispatch({ type: 'FETCH_POSTS', posts }))
  1. 现在,这个 Effect Hook 应该只在组件挂载时触发,所以我们传递一个空数组来作为 useEffect 的第二个参数:
}, [])

正如我们所看到的,现在从服务器请求获取帖子!我们可以通过浏览器的“DevTools 网络”选项卡来查看请求:

uploads/2102/requested.jpg
从我们的服务器请求的帖子!

现在正在从后端服务器请求这些帖子。在下一节中,我们将使用 axiosreact-request-hook 从我们的服务器请求资源。

示例代码

示例代码可以在 Chapter06/chapter6_2 文件夹中找到。

只需运行 npm install 即可安装所有依赖项,运行 npm start 启动应用程序,然后在您的浏览器中访问 http://localhost:3000(如果它没有自动打开)


(节选)

1

评论 (0)

取消