React 自定义 Hooks 教程 - 创建 useOnline、测试并发布
这最初发布在我的个人博客上
在本教程中,我们将介绍如何创建一个简单的自定义 React hook,在本地进行测试,然后将其发布到 NPM。我们将创建的 React hook 可以useOnline
检测用户是否离线,并向他们显示离线消息。
实现之后,我们将检查如何在本地测试它,然后将其发布到 NPM。
如果您阅读本教程只是为了学习如何创建自定义钩子并将其用于现有项目,而不打算将其作为包发布到 NPM,那么您可以在本教程的测试和发布部分之前停止学习。您可能也不需要完成设置部分。
本教程的代码可在此 GitHub 存储库中找到。
什么是自定义钩子?
自定义 hooks 包含特定的逻辑,这些逻辑会利用 React 的 hooks,例如 useState、useEffect 等。通常,当项目的某个部分可复用且需要使用 React hooks 时,你会创建自定义 hooks。因此,你创建的自定义 hooks 可以在整个项目中使用,就像使用 React hooks 一样。它也应该以 开头use
。
设置
让我们首先创建一个新目录并切换到该目录:
mkdir use-online
cd use-online
然后,我们将初始化我们的 NPM 项目:
npm init
您必须输入一些信息,package.json
例如包名称、描述、作者、主要条目等...您现在可以使用默认设置。
完成后,您将获得一个空的 NPM 包。现在让我们安装用于开发自定义 React hook 的依赖项:
npm i --save-dev react @babel/cli copyfiles
由于我们正在开发一个自定义钩子,所以我们安装了 React。我们还安装了 Babel 的 CLI,以便稍后构建代码。此外,我们还安装了 copyfiles,在准备发布包时也会用到它。
一旦完成这些,我们就可以实现我们的自定义钩子了。
实现 useOnline
正如我在开头提到的,useOnline
它会检测用户是否在线或离线。这意味着它会管理用户的连接状态,监听用户连接的任何变化并进行相应的更新。
因此,useOnline
我们将利用 来useStatus
跟踪用户的连接情况,并注册事件useEffect
监听器来设置相应的状态。最终,我们只需返回状态即可,这样我们就可以在其它组件中使用它来跟踪用户的连接情况,而无需重复其背后的逻辑。online
offline
useOnline
让我们首先创建包含自定义钩子的文件。创建src/useOnline.js
内容如下:
import { useState, useEffect } from 'react'
function useOnline () {
}
export default useOnline
我们只是导入useState
并useEffect
稍微使用它们,声明自定义钩子useOnline
并将其导出。
现在,让我们来看看钩子的代码。首先,让我们创建保存用户连接的状态:
function useOnline () {
const [online, setOnline] = useState(navigator.onLine);
}
online
将保存用户的连接状态,它是一个布尔值。如果用户在线,则为 true;如果用户不在线,则为 false。其初始值,我们使用navigator.onLine
返回浏览器在线状态的值。
接下来,我们需要监听online
和offline
事件。online
事件在用户上线时发生, 事件offline
在用户离线时发生。要添加监听器,我们将使用useEffect
:
function useOnline () {
const [online, setOnline] = useState(navigator.onLine)
useEffect (() => {
window.addEventListener('online', function () {
//TODO change state to online
});
window.addEventListener('offline', function () {
//TODO change state to offline
});
}, [])
}
因此,我们在回调函数中添加了事件监听器,online
并offline
在回调函数中设置了事件监听器useEffect
。我们还传递了一个空数组作为第二个参数useEffect
。这确保了回调函数仅在组件挂载时才会被调用。
现在,让我们在每个监听器中添加逻辑。我们只需要根据事件更改 的值online
。为此,我们将使用setOnline
:
useEffect (() => {
window.addEventListener('online', function () {
setOnline(true)
});
window.addEventListener('offline', function () {
setOnline(false)
});
}, [])
非常简单。我们的代码现在为online
和事件添加了一个事件监听器,它会根据用户的连接情况offline
改变状态的值。online
在添加事件监听器或任何类型的订阅时,我们需要确保在组件卸载后进行清理。为此,我们返回一个函数,useEffect
该函数会在卸载时移除事件监听器。
由于我们将使用removeEventListener
删除事件监听器,它将我们要移动的事件监听器作为第二个参数,因此让我们将事件监听器删除为我们可以引用的函数:
function offlineHandler () {
setOnline(false)
}
function onlineHandler () {
setOnline(true)
}
useEffect (() => {
window.addEventListener('online', onlineHandler)
window.addEventListener('offline', offlineHandler)
return () => {
window.removeEventListener('online', onlineHandler)
window.removeEventListener('offline', offlineHandler)
}
}, [])
我们将事件监听器移到了函数外部useEffect
(您也可以将它们添加到内部),并且将它们作为事件监听器传递到addEventListener
和事件的removeEventListener
内部。useEffect
online
offline
自定义钩子中最后要做的就是返回正在修改的状态。这样,我们就可以在其他组件中使用此状态,并将其背后的所有逻辑都集中在一个地方。
因此,完整的代码useOnline
如下:
import { useState, useEffect } from 'react'
function useOnline () {
const [online, setOnline] = useState(navigator.onLine)
function offlineHandler () {
setOnline(false)
}
function onlineHandler () {
setOnline(true)
}
useEffect (() => {
setOnline(navigator.onLine)
window.addEventListener('online', onlineHandler)
window.addEventListener('offline', offlineHandler)
return () => {
window.removeEventListener('online', onlineHandler)
window.removeEventListener('offline', offlineHandler)
}
}, [])
return online
}
export default useOnline;
就这样!我们创建了一个自定义钩子,它利用 React 钩子(例如useState
和 )useEffect
来确定用户的连接状态。
准备 NPM 包
如果您想在 NPM 上发布自定义钩子,您需要准备要发布和使用的包。有一些事情需要做,尤其是在 中package.json
。
首先,我们安装了@babel/cli
和copyfiles
。在这里我们将使用它们。
封装信息
首次运行时,npm init
系统会要求您输入一些信息,例如包名称、描述、作者、版本、许可证等。如果您使用了默认信息,或者想要更改这些信息,请确保在发布之前进行更改。您可以在package.json
文件中进行更改。
请注意,name
inpackage.json
是用户安装时使用的包名。因此,请确保它与你想要的包名完全一致。
依赖项
发布软件包时,请确保正确列出所需的依赖项。如果某些依赖项仅在开发期间需要,并且在使用时无需安装,请将其包含在 下devDependencies
。
在我们的例子中,我们应该有:
"devDependencies": {
"react": "^17.0.1",
"@babel/cli": "^7.13.14",
"copyfiles": "^2.4.1"
}
请注意,您的项目中的版本可能有所不同,但这没关系。
还有一点需要注意:在一个 React 项目中,只react
允许安装或实例一个。这意味着在项目中安装 React 时,你的包不应该同时安装 React。
因此,让我们react
将其改为像这样的对等依赖关系:
"peerDependencies": {
"react": "^16.8.0 || ^17.0.1"
},
"devDependencies": {
"@babel/cli": "^7.13.14",
"copyfiles": "^2.4.1"
}
在 中添加依赖项时peerDependencies
,react
项目中使用的包含此依赖项的包将被使用,而不是安装新的包。我们还允许版本至少为16.8.0
React Hooks 引入时的版本。
脚本
为了确保我们的包可以使用,我们将添加使用以下命令构建我们的 React 自定义钩子的脚本babel
:
"scripts": {
"prebuild": "npm i",
"build": "babel src --out-dir dist"
},
现在,每当我们运行 时build
,prebuild
都会首先运行以确保所需的依赖项已安装,然后构建脚本将编译我们src
目录(即useOnline.js
)中的 Javascript 文件并将结果输出到 中dist
。
主要的
如果我们希望我们的包像这样使用:
import useOnline from 'use-online'
然后,我们需要指定要导出的内容以及导入的文件。导入的文件就是main
我们包里的文件。
在我们的例子中,它将是build
脚本的输出:
"main": "dist/useOnline.js"
文件
发布软件包时,默认情况下,它会发布从根目录开始的所有文件和目录。这会显著增加软件包的大小,尤其是在软件包中存在大量冗余文件或不必要的文件时。
在我们的示例中,如果您查看GitHub 仓库,就会看到一个example
目录。我们稍后会讲到这个目录里的内容,但很多时候,您可能会有一些示例、图片或其他文件,这些文件在包开发过程中是必需的,但在发布包时却不是。
为了减小包大小并确保仅包含相关文件,我们使用以下files
键:
"files": [
"dist"
],
files
接受一个数组,该数组包含发布后应包含在包中的所有文件或目录。在本例中,它只是dist
保存构建代码的目录。
类型
这一项完全是可选的,我使用的是最简单的形式。您可以为您的包添加一个 Typescript 声明。为此,我们将创建src/useOnline.d.ts
以下内容:
declare module 'use-online' {
export default function useOnline (): boolean
}
这将声明导出返回布尔值(即在线状态)的use-online
函数的模块。useOnline
接下来,我们将在中添加一个新脚本package.json
:
"scripts": {
"prebuild": "npm i",
"build": "babel src --out-dir dist",
"postbuild": "copyfiles -u 1 ./src/useOnline.d.ts ./dist"
},
脚本完成postbuild
后将运行。它将复制到目录中。build
src/useOnline.d.ts
dist
最后,我们将添加types
密钥package.json
:
"types": "dist/useOnline.d.ts",
这会使你的包成为一个 Typescript 包,尽管在 Typescript 包中你实际上不会这样做。这只是一个简单的操作方式。
在本地测试我们的自定义钩子
如果您要将自定义钩子添加到现有项目中,那么您可能只需在那里测试即可。但是,如果您要创建自定义钩子并将其发布到线上,并且希望将其作为单独的包进行测试,那么本节非常适合您。
在我为本教程创建的GitHub 仓库中,你可以看到一个example
文件夹。这个文件夹包含一个使用 GitHub 构建的网站,create-react-app
用于测试use-online
包含钩子的包useOnline
。
如果您没有要测试的项目use-online
,让我们通过运行以下命令创建一个仅用于此目的的项目:
npx create-react-app example
这将创建一个新目录example
,其中将保存使用 React 构建的单页应用程序(SPA)。
在进入该目录之前,我们先来看看use-online
如果它实际上不是 NPM 上的包,我们该如何使用。你可能已经知道,你可以使用install
或i
命令安装 NPM 上的任何包,如下所示:
npm install <PACKAGE_NAME>
但是,我们如何安装一个仅在本地可用的软件包呢?我们将为您提供链接。
npm-link
允许我们在机器的全局文件夹中创建包的符号链接。这样,我们就可以在机器上的其他项目中“安装”本地包,用于测试等目的。
我们要做的是创建一个链接use-online
,然后在我们刚刚创建的项目中使用它example
。
在根目录中use-online
运行以下命令:
npm link
完成后,将创建指向该包的符号链接。现在我们可以切换到示例目录,并use-online
通过链接来“安装”该包:
cd example
npm link use-online
链接完成后,您现在可以use-online
像安装其他 NPM 包一样在项目中使用它。您所做的任何更改use-online
都会自动反映在包中。
在我们使用它之前use-online
,让我们进入它的根目录并运行构建命令:
npm run build
这将运行 NPM 安装,使用 编译代码babel
,然后(如果您按照 typescript 部分操作)将 typescript 声明文件复制到dist
我建议在测试之前删除 目录node_modules
。正如我们之前提到的,peerDependencies
如果要安装到的项目中use-online
已经安装了 React,则不会安装 。但是,当我们运行构建命令时,该包是独立的,并且没有react
安装任何依赖项,因此它安装了react
。由于我们链接到它而不是实际将它安装在 中example
,node_modules
的目录use-online
将位于node_modules
的目录中example
,这将导致react
中出现两个实例example
。因此,请确保node_modules
在use-online
测试之前删除 。
我们只需在中添加三行代码即可example/src/App.js
。首先,我们将导入自定义钩子:
import useOnline from 'use-online'
其次,在App
组件内部,我们将使用useOnline
钩子来获取online
状态:
function App() {
const online = useOnline()
//... rest of the code
}
第三,也是最后一点,我们将在渲染部分添加一个条件,向用户显示他们处于离线状态:
return (
<div className="App">
<header className="App-header">
{!online && <p>You're Offline</p>}
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
注意我们添加的行:
{!online && <p>You're Offline</p>}
当online
为 false 时,表示用户处于离线状态,因此我们会向其显示该消息。请记住,根据用户连接状态更改状态的逻辑实际上是在 内部完成的useOnline
。我们只需使用返回online
值,其他所有操作都在自定义钩子内部完成。
现在让我们通过运行以下命令启动开发服务器:
npm start
它只是我们每次开始新create-react-app
项目时看到的默认 React 页面:
最好的测试方法useOnline
是模拟离线。具体操作如下:打开开发者工具,然后前往“应用程序”选项卡
如您所见,有一个复选框用于模拟离线浏览器。它用于测试 Service Worker,但它仍然适用于任何类型的用户连接测试。
选中“离线”复选框后,您应该会看到我们添加的“您处于离线状态”消息:
我们的自定义钩子成功了!尝试开启和关闭它。勾选“离线”复选框后,消息会显示。取消勾选后,消息会被移除。
发布你的自定义钩子
现在我们已经完成了自定义钩子的测试,并且配置了包中的所有内容,我们准备在 NPM 上发布它。
首先,确保你有一个NPM帐户。如果没有,你需要先创建一个。
在您的终端运行:
npm login
您需要输入用户名、密码和邮箱地址。如果全部正确,您将通过身份验证并获得发布软件包的授权。
在包的根目录中,运行:
npm publish
除非出现任何错误,否则你只需要这样做!此命令运行完成后,你的包就会生效。
如果您收到有关具有相似名称的现有包的错误,请确保重命名其中的包package.json
:
"name": "NEW_PACKAGE_NAME"
然后再试一次。
如果你的软件包发布成功,你将收到一封电子邮件通知,你可以继续在 NPM 上查看它。然后你可以在项目内部运行:
npm install PACKAGE_NAME
它将像任何软件包一样被安装!
更新你的软件包
如果您稍后决定修复一些错误或对包进行任何更改并想要更新它,只需在包的根目录中运行:
npm version TYPE
其中 TYPE 可以是patch
(用于修复小错误)、minor
(用于小更改)或 (用于大更改)。您可以在此处major
阅读更多相关信息。