在前面的章节中,我们学习了如何使用 React Context 作为手动传递 props 的替代方案。我们了解了context provider、consumer 以及如何将 Hooks 用作 context Consumer。接下来,我们了解了控制反转作为 context 的替代方案。最后,我们在博客应用程序中使用 context 实现了主题和全局状态。
在本章中,我们将设置一个简单的后端服务器,它将使用 json-server
工具从 JavaScript 对象表示法 (JSON) 文件生成服务。然后,我们将通过将 Effect Hook 与 State Hook 结合使用来实现请求资源。接下来,我们将使用 axios
和 react-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。
观看以下视频,了解代码的实际应用:
请注意,强烈建议您自己编写代码。不要简单地运行已提供的代码示例。重要的是你自己编写代码,以便你能够正确学习和理解。但是,如果遇到任何问题,始终可以参考代码示例。
现在,让我们从本章开始。
使用 Hook 请求资源
在本节中,我们将学习如何使用 Hook 从服务器请求资源。首先,我们将仅使用 JavaScript fetch 函数和 useEffect
/useState
Hook 来实现请求。然后,我们将学习如何组合使用 axios
和 react-request-hook
库来请求资源。
设置虚拟服务器
在实现请求之前,我们需要创建一个后端服务器。由于我们目前专注于用户界面,因此我们将设置一个虚拟服务器来测试请求。我们将使用 json-server
工具从 JSON 文件创建完整的表述性状态传输 (REST) API。
创建 db.json 文件
为了能够使用 json 服务器 工具,首先我们需要创建一个 db.json
文件,该文件将包含服务器的完整数据库。json-server
工具将允许您执行以下操作:
GET
请求,用于从文件中获取数据POST
请求,将新数据插入文件PUT
和PATCH
请求,修改现有数据DELETE
请求,删除数据
对于所有修改操作(POST
、PUT
、PATCH
和 DELETE
),更新后的文件将由工具自动保存。
我们可以对帖子使用现有的结构,将其定义为 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 文件:
- 在应用程序文件夹的根目录中创建一个新的
server/
目录。 - 创建包含以下内容的 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
工具并使用它启动我们的后端服务器:
- 首先,我们将通过
npm
安装json-server
工具:
npm install --save json-server
- 现在,我们可以通过调用以下命令来启动后端服务器:
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 工作了并提供一个帖子的数据!
如我们所见,该工具从数据库 JSON 文件为我们创建了一个完整的 REST API!
配置 package.json
接下来,我们需要调整我们的 package.json
文件,以便启动服务器以及我们的客户端(通过运行 webpack-dev-server )。
让我们开始调整 package.json
文件:
- 首先,我们创建一个名为
start:server
的新包脚本,方法是将其插入package.json
文件的脚本部分。我们还确保更改端口,使其不会在与客户端相同的端口上运行:
"scripts": {
"start:server": "npx json-server --watch server/db.json --port 4000",
"start": "react-scripts start",
- 然后,我们将启动脚本重命名为
start:client
:
"scripts": {
"start:server": "npx json-server --watch server/db.json",
"start:client": "react-scripts start",
- 接下来,我们安装一个名为
concurrently
的工具,它让我们同时启动服务器和客户端:
npm install --save concurrently
- 现在,我们可以通过使用并发命令定义一个新的启动脚本,然后将服务器和客户端命令作为参数传递给它:
"scripts": {
"start": "npx concurrently \"npm run start:server\" \"npm run start:client\"
现在,运行 npm start
将运行客户端以及后端服务器。
配置代理
最后,我们必须定义一个代理,以确保我们可以从与客户端相同的 统一资源定位器 (URL) 请求我们的 API。这是必需的,否则,我们将不得不处理跨站点请求,这些请求处理起来有点复杂。我们将定义一个代理,该代理将来自 http://localhost:3000/api/
请求转发到 http://localhost:4000/
。
现在,让我们配置代理:
- 首先,我们必须安装
http-proxy-middleware
包:
npm install --save http-proxy-middleware
- 然后,我们创建一个新的
src/setupProxy.js
文件,其中包含以下内容:
const proxy = require('http-proxy-middleware')
module.exports = function (app) {
app.use(proxy('/api', {
接下来,我们必须定义代理的目标,它将是后端服务器,运行在
http://localhost:4000
。target: 'http://localhost:4000',
- 最后,我们必须定义一个路径重写规则,该规则将在请求转发到我们的服务器之前删除 /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
参数,以便找到具有给定用户名和密码组合的用户。
我们现在将为我们的应用程序定义自定义登录路由:
- 创建一个包含以下内容的新
server/routes.json
文件:
{
"/login/:username/:password": "/users?username=:username&password=:password"
}
- 然后,调整
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
,这将返回一个用户对象,登录成功了!
我们可以看到用户对象在浏览器中以文本形式返回:
直接在浏览器中访问我们的自定义路由
如我们所见,访问我们的自定义路由有效!我们现在可以使用它来登录用户。
示例代码
示例代码可以在 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 实现请求主题:
- 在
src/ChangeTheme.js
文件中,调整 React import 语句以导入useEffect
和useState
Hook:
import React, { useEffect, usestate } from 'react'
- 删除
THEMES
常量,即以下所有代码:
const THEMES = [
{ primaryColor: 'deepskyblue', secondaryColor: 'coral' },
{ primaryColor: 'orchid', secondaryColor: 'mediumseagreen' }
]
- 在
ChangeTheme
组件中,定义一个新的useState
Hook 以存储主题:
export default function ChangeTheme({ theme, setTheme }) {
const [themes, setThemes] = usestate([])
- 然后定义一个
useEffect
Hook,我们将在其中发出请求:
useEffect(() => {
- 在这个 Hook 中,我们使用
fetch
来请求资源;本例中我们请求/api/themes
:
fetch('/api/themes')
- Fetch 使用了 Promise API;因此,我们可以使用
.then()
来处理结果。首先,我们必须将结果解析为 JSON:
.then(result => result.json())
- 最后,我们使用请求中的 themes 数组调用
setThemes
:
.then(themes => setThemes(themes))
我们还可以将上述函数缩短为 .then(setThemes),因为我们只是从 .then() 传递主题参数。
- 目前,这个 Effect Hook 应该只在组件挂载时触发,因此我们将一个空数组作为第二个参数传递给
useEffect
。这可确保 Effect Hook 没有依赖项,因此仅在组件挂载时触发:
}, [])
- 现在,剩下要做的就是将
THEMES
常量替换为 Hook 中的themes
值:
{themes.map(t =>
正如我们所看到的,现在有三个主题可用,它们都通过我们的服务器从我们的数据库加载:
使用 Hook 从我们的服务器加载三个主题!
我们的主题现在从后端服务器加载,我们可以继续通过 Hook 请求帖子。
使用 Effect 和 Reducer Hook 请求
我们现在将使用后端服务器来请求 posts 数组,而不是将其硬编码为 postsReducer 的默认值。
让我们使用 Effect Hook 和 Reducer Hook 实现请求帖子:
- 从
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' }
]
- 将
useReducer
函数中的defaultPosts
常量替换为空数组:
const [state, dispatch] = useReducer(appReducer, {
user: '',
posts: []
})
- 在
src/reducers.js
中,在postsReducer
函数中定义一个名为FETCH_POSTS
的 action 新类型。此 action 类型将用新的帖子数组替换当前状态:
function postsReducer (state, action) {
switch (action.type) {
case 'FETCH_POsTs':
return action.posts
- 在
src/App.js
中,定义一个新的useEffect
Hook,它位于当前 Hook 之前:
useEffect(() => {
- 在这个 Hook 中,我们再次使用
fetch
来请求资源;本例中我们请求/api/posts
:
fetch('/api/posts')
.then(result => result.json())
- 最后,我们使用请求中的
posts
数组调度一个FETCH_POSTS
action:
.then(posts => dispatch({ type: 'FETCH_POSTS', posts }))
- 现在,这个 Effect Hook 应该只在组件挂载时触发,所以我们传递一个空数组来作为
useEffect
的第二个参数:
}, [])
正如我们所看到的,现在从服务器请求获取帖子!我们可以通过浏览器的“DevTools 网络”选项卡来查看请求:
从我们的服务器请求的帖子!
现在正在从后端服务器请求这些帖子。在下一节中,我们将使用 axios
和 react-request-hook
从我们的服务器请求资源。
示例代码
示例代码可以在 Chapter06/chapter6_2 文件夹中找到。
只需运行 npm install
即可安装所有依赖项,运行 npm start
启动应用程序,然后在您的浏览器中访问 http://localhost:3000
(如果它没有自动打开)
(节选)
评论 (0)