From 7a662a9288b424965c79a30b635697acb5c35307 Mon Sep 17 00:00:00 2001
From: xugaoyi <894072666@qq.com>
Date: Tue, 6 Apr 2021 19:12:49 +0800
Subject: [PATCH] =?UTF-8?q?blog:=20=E6=9B=B4=E6=96=B0React=20hook=E7=AC=94?=
=?UTF-8?q?=E8=AE=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/《React》笔记/03.Hook/01.Hook.md | 54 ----
docs/《React》笔记/03.Hook/01.Hook概述.md | 296 ++++++++++++++++++
.../03.Hook/02.使用State Hook.md | 144 +++++++++
.../03.Hook/03.使用Effect Hook.md | 269 ++++++++++++++++
docs/《React》笔记/03.Hook/04.Hook规则.md | 61 ++++
docs/《React》笔记/03.Hook/05.自定义 Hook.md | 83 +++++
6 files changed, 853 insertions(+), 54 deletions(-)
delete mode 100644 docs/《React》笔记/03.Hook/01.Hook.md
create mode 100644 docs/《React》笔记/03.Hook/01.Hook概述.md
create mode 100644 docs/《React》笔记/03.Hook/02.使用State Hook.md
create mode 100644 docs/《React》笔记/03.Hook/03.使用Effect Hook.md
create mode 100644 docs/《React》笔记/03.Hook/04.Hook规则.md
create mode 100644 docs/《React》笔记/03.Hook/05.自定义 Hook.md
diff --git a/docs/《React》笔记/03.Hook/01.Hook.md b/docs/《React》笔记/03.Hook/01.Hook.md
deleted file mode 100644
index 671cfb3..0000000
--- a/docs/《React》笔记/03.Hook/01.Hook.md
+++ /dev/null
@@ -1,54 +0,0 @@
----
-title: Hook
-date: 2021-03-27 17:48:03
-permalink: /pages/4c13b9/
-categories:
- - 《React》笔记
- - Hook
-tags:
- - React
----
-# 01. Hook
-
-Hook可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
-
-
-
-## useState使用方式
-
-**模式:**
-
-```js
-const [<取值>, <设值>] = useState(<初始值>)
-```
-
-上面的三个值均可自定义名称,分别是:
-
-- state取值变量名
-- state设值方法名
-- 初始值
-
-
-
-**例子:**
-
-```jsx
-import React, { useState } from 'react';
-
-function Example() {
- // 声明一个新的叫做 “count” 的 state 变量
- const [count, setCount] = useState(0);
-
- return (
-
-
You clicked {count} times
-
-
- );
-}
-```
-
-
-
diff --git a/docs/《React》笔记/03.Hook/01.Hook概述.md b/docs/《React》笔记/03.Hook/01.Hook概述.md
new file mode 100644
index 0000000..a0ecac2
--- /dev/null
+++ b/docs/《React》笔记/03.Hook/01.Hook概述.md
@@ -0,0 +1,296 @@
+---
+title: Hook概述
+date: 2021-03-31 11:30:03
+permalink: /pages/4c13b9/
+categories:
+ - 《React》笔记
+ - Hook
+tags:
+ - React
+---
+# 01. Hook概述
+
+Hook可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
+
+
+
+## 什么是 Hook?
+
+Hook 是一个特殊的函数,可以让你在函数组件里“钩入” React **state** 及 **生命周期** 等特性的函数。
+
+> 1. Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。
+>
+> 2. React 内置了一些像 `useState` 这样的 Hook。你也可以创建你自己的 Hook 来复用不同组件之间的状态逻辑。我们会先介绍这些内置的 Hook。
+
+
+
+## useState(状态钩子)
+
+**模式:**
+
+```js
+const [<取值>, <设值>] = useState(<初始值>)
+```
+
+上面的三个值均可自定义名称,分别是:
+
+- state取值变量名
+- state设值方法名
+- 初始值
+ - 初始值可以是数字、字符串、对象、数组等
+
+
+
+**例子:**
+
+```jsx
+import React, { useState } from 'react';
+
+function Example() {
+ // 声明一个新的叫做 “count” 的 state 变量
+ const [count, setCount] = useState(0);
+
+ return (
+
+
You clicked {count} times
+
+
+ );
+}
+```
+
+
+
+### 声明多个 state 变量
+
+```jsx
+function ExampleWithManyStates() {
+ // 声明多个 state 变量!
+ const [age, setAge] = useState(42);
+ const [fruit, setFruit] = useState('banana');
+ const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
+ // ...
+}
+```
+
+
+
+## Effect Hook (副作用钩子)
+
+你之前可能已经在 React 组件中执行过**数据获取、订阅 或 者手动修改 DOM**。我们统一把这些操作称为“**副作用**”,或者简称为“作用”。
+
+### useEffect(生命周期钩子)
+
+`useEffect` 就是一个 Effect Hook,**给函数组件增加了操作副作用的能力**。它跟 class 组件中的 `componentDidMount`、`componentDidUpdate` 和 `componentWillUnmount` 具有相同的用途,只不过被合并成了一个 API。(我们会在[使用 Effect Hook](https://zh-hans.reactjs.org/docs/hooks-effect.html) 里展示对比 `useEffect` 和这些方法的例子。)
+
+
+
+例如,下面这个组件在 React 更新 DOM 后会设置一个页面标题:
+
+```jsx
+import React, { useState, useEffect } from 'react';
+function Example() {
+ const [count, setCount] = useState(0);
+
+ // 相当于 componentDidMount 和 componentDidUpdate:
+ useEffect(() => {
+ // 第一次渲染DOM以及更新DOM后执行此函数
+ // 使用浏览器的 API 更新页面标题
+ document.title = `You clicked ${count} times`;
+ });
+ return (
+
+
You clicked {count} times
+
+
+ );
+}
+```
+
+当你调用 `useEffect` 时,就是在告诉 React **在完成对 DOM 的更改后运行你的“副作用”函数**。由于副作用函数是在组件内声明的,所以它们可以访问到组件的 props 和 state。默认情况下,React 会在每次渲染后调用副作用函数 —— **包括**第一次渲染的时候。
+
+
+
+### “清除”副作用
+
+副作用函数还可以通过返回一个函数来指定如何“清除”副作用。例如,在下面的组件中使用副作用函数来订阅好友的在线状态,并通过取消订阅来进行清除操作:
+
+```jsx
+import React, { useState, useEffect } from 'react';
+
+function FriendStatus(props) {
+ const [isOnline, setIsOnline] = useState(null);
+
+ function handleStatusChange(status) {
+ setIsOnline(status.isOnline);
+ }
+
+ useEffect(() => { // 组件挂载完成和更新完成后执行
+ // 创建订阅
+ ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
+ return () => { // 组件销毁时执行
+ // 取消订阅
+ ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
+ };
+ });
+ if (isOnline === null) {
+ return 'Loading...';
+ }
+ return isOnline ? 'Online' : 'Offline';
+}
+```
+
+在这个示例中,React 会在组件销毁时取消对 `ChatAPI` 的订阅,然后在后续渲染时重新执行副作用函数。(如果传给 `ChatAPI` 的 `props.friend.id` 没有变化,你也可以[告诉 React 跳过重新订阅](https://zh-hans.reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects)。)
+
+跟 `useState` 一样,你可以在组件中多次使用 `useEffect` :
+
+```jsx
+function FriendStatusWithCounter(props) {
+ const [count, setCount] = useState(0);
+ useEffect(() => {
+ document.title = `You clicked ${count} times`;
+ });
+
+ const [isOnline, setIsOnline] = useState(null);
+ useEffect(() => {
+ ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
+ return () => {
+ ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
+ };
+ });
+
+ function handleStatusChange(status) {
+ setIsOnline(status.isOnline);
+ }
+ // ...
+```
+
+通过使用 Hook,你可以把组件内相关的副作用组织在一起(例如创建订阅及取消订阅),而不要把它们拆分到不同的生命周期函数里。
+
+> 详细说明
+>
+> 你可以在这一章节了解更多关于 `useEffect` 的内容:[使用 Effect Hook](https://zh-hans.reactjs.org/docs/hooks-effect.html)
+
+
+
+## Hook 使用规则
+
+Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则:
+
+- 只能在**函数最外层**调用 Hook。不要在循环、条件判断或者子函数中调用。
+- 只能在 **React 的函数组件**中调用 Hook。不要在其他 JavaScript 函数中调用。(还有一个地方可以调用 Hook —— 就是自定义的 Hook 中,我们稍后会学习到。)
+
+同时,我们提供了 [linter 插件](https://www.npmjs.com/package/eslint-plugin-react-hooks)来自动执行这些规则。这些规则乍看起来会有一些限制和令人困惑,但是要让 Hook 正常工作,它们至关重要。
+
+> 详细说明
+>
+> 你可以在这章节了解更多关于这些规则的内容:[Hook 使用规则](https://zh-hans.reactjs.org/docs/hooks-rules.html)。
+
+
+
+## 自定义 Hook (Hook的使用逻辑封装)
+
+有时候我们会想要在组件之间重用一些状态逻辑。目前为止,有两种主流方案来解决这个问题:[高阶组件](https://zh-hans.reactjs.org/docs/higher-order-components.html)和 [render props](https://zh-hans.reactjs.org/docs/render-props.html)。自定义 Hook 可以让你在不增加组件的情况下达到同样的目的。
+
+前面,我们介绍了一个叫 `FriendStatus` 的组件,它通过调用 `useState` 和 `useEffect` 的 Hook 来订阅一个好友的在线状态。假设我们想在另一个组件里重用这个订阅逻辑。
+
+首先,我们把这个逻辑抽取到一个叫做 `useFriendStatus` 的自定义 Hook 里:
+
+```jsx
+import React, { useState, useEffect } from 'react';
+
+// 逻辑抽取,方便复用
+function useFriendStatus(friendID) {
+ // 每次调用时的state都是独立的
+ const [isOnline, setIsOnline] = useState(null);
+
+ function handleStatusChange(status) {
+ setIsOnline(status.isOnline);
+ }
+
+ useEffect(() => {
+ ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
+ return () => {
+ ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
+ };
+ });
+
+ return isOnline;
+}
+```
+
+它将 `friendID` 作为参数,并返回该好友是否在线:
+
+现在我们可以在两个组件中使用它:
+
+```jsx
+function FriendStatus(props) {
+ const isOnline = useFriendStatus(props.friend.id);
+ if (isOnline === null) {
+ return 'Loading...';
+ }
+ return isOnline ? 'Online' : 'Offline';
+}
+function FriendListItem(props) {
+ const isOnline = useFriendStatus(props.friend.id);
+ return (
+
+ {props.friend.name}
+
+ );
+}
+```
+
+**每个组件间的 state 是完全独立的。Hook 是一种复用*状态逻辑*的方式,它不复用 state 本身**。事实上 Hook 的每次*调用*都有一个完全独立的 state —— 因此你可以在单个组件中多次调用同一个自定义 Hook。
+
+自定义 Hook 更像是一种约定而不是功能。**如果函数的名字以 “`use`” 开头并调用其他 Hook,我们就说这是一个自定义 Hook**。 `useSomething` 的命名约定可以让我们的 linter 插件在使用 Hook 的代码中找到 bug。
+
+你可以创建涵盖各种场景的自定义 Hook,如表单处理、动画、订阅声明、计时器,甚至可能还有更多我们没想到的场景。我们很期待看到 React 社区会出现什么样的自定义 Hook。
+
+> 详细说明
+>
+> 我们会在这一章节介绍更多关于自定义 Hook 的内容: [创建你自己的 Hook](https://zh-hans.reactjs.org/docs/hooks-custom.html)。
+
+
+
+## 其他 Hook
+
+除此之外,还有一些使用频率较低的但是很有用的 Hook。比如,[`useContext`](https://zh-hans.reactjs.org/docs/hooks-reference.html#usecontext) 让你不使用组件嵌套就可以订阅 React 的 Context。
+
+```jsx
+function Example() {
+ const locale = useContext(LocaleContext);
+ const theme = useContext(ThemeContext);
+ // ...
+}
+```
+
+另外 [`useReducer`](https://zh-hans.reactjs.org/docs/hooks-reference.html#usereducer) 可以让你通过 reducer 来管理组件本地的复杂 state。
+
+```jsx
+function Todos() {
+ const [todos, dispatch] = useReducer(todosReducer); // ...
+```
+
+> 详细说明
+>
+> 你可以在这一章节了解更多关于所有内置 Hook 的内容:[Hook API 索引](https://zh-hans.reactjs.org/docs/hooks-reference.html)。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/《React》笔记/03.Hook/02.使用State Hook.md b/docs/《React》笔记/03.Hook/02.使用State Hook.md
new file mode 100644
index 0000000..681db3e
--- /dev/null
+++ b/docs/《React》笔记/03.Hook/02.使用State Hook.md
@@ -0,0 +1,144 @@
+# 02. 使用State Hook
+
+```jsx
+import React, { useState } from 'react';
+
+function Example() {
+ // 声明一个叫 "count" 的 state 变量
+ const [count, setCount] = useState(0);
+
+ return (
+
+
You clicked {count} times
+
+
+ );
+}
+```
+
+我们将通过将这段代码与一个等价的 class 示例进行比较来开始学习 Hook。
+
+
+
+## 等价的Class组件示例
+
+```jsx
+class Example extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ count: 0
+ };
+ }
+
+ render() {
+ return (
+
+
You clicked {this.state.count} times
+
+
+ );
+ }
+}
+```
+
+state 初始值为 `{ count: 0 }` ,当用户点击按钮后,我们通过调用 `this.setState()` 来增加 `state.count`。整个章节中都将使用该 class 的代码片段做示例。
+
+
+
+### Hook 在 class 内部是不起作用
+
+**Hook 在 class 内部是不起作用的**。但你可以使用它们来取代 class 。
+
+
+
+
+
+## 调用 `useState` 方法的时候做了什么?
+
+它定义一个 “state 变量”。我们的变量叫 `count`, 但是我们可以叫他任何名字,比如 `banana`。这是一种在函数调用时保存变量的方式 —— `useState` 是一种新方法,它与 class 里面的 `this.state` 提供的功能完全相同。**一般来说,在函数退出后变量就会”消失”,而 state 中的变量会被 React 保留。**
+
+
+
+## `useState` 需要哪些参数?
+
+`useState()` 方法里面**唯一的参数就是初始 state**。不同于 class 的是,我们可以按照需要使用数字或字符串对其进行赋值,而不一定是对象。在示例中,只需使用数字来记录用户点击次数,所以我们传了 `0` 作为变量的初始 state。(如果我们想要在 state 中存储两个不同的变量,只需调用 `useState()` 两次即可。)
+
+
+
+## `useState` 方法的返回值是什么?
+
+返回值为:当前 state 以及更新 state 的函数。这就是我们写 `const [count, setCount] = useState()` 的原因。这与 class 里面 `this.state.count` 和 `this.setState` 类似,唯一区别就是你需要成对的获取它们。
+
+
+
+
+
+## 理解useState
+
+既然我们知道了 `useState` 的作用,我们的示例应该更容易理解了:
+
+```jsx
+import React, { useState } from 'react';
+
+function Example() {
+ // 声明一个叫 "count" 的 state 变量 const [count, setCount] = useState(0);
+```
+
+我们声明了一个叫 `count` 的 state 变量,然后把它设为 `0`。**React 会在重复渲染时记住它当前的值,并且提供最新的值给我们的函数。**我们可以通过调用 `setCount` 来更新当前的 `count`。
+
+
+
+> 注意
+>
+> 你可能想知道:为什么叫 `useState` 而不叫 `createState`?
+>
+> “Create” 可能不是很准确,因为 state 只在组件首次渲染的时候被创建。在下一次重新渲染时,`useState` 返回给我们当前的 state。否则它就不是 “state”了!这也是 Hook 的名字*总是*以 `use` 开头的一个原因。我们将在后面的 [Hook 规则](https://zh-hans.reactjs.org/docs/hooks-rules.html)中了解原因。
+
+
+
+## 使用多个 state 变量
+
+将 state 变量声明为一对 `[something, setSomething]` 也很方便,因为如果我们想使用多个 state 变量,它允许我们给不同的 state 变量取不同的名称:
+
+```jsx
+function ExampleWithManyStates() {
+ // 声明多个 state 变量
+ const [age, setAge] = useState(42);
+ const [fruit, setFruit] = useState('banana');
+ const [todos, setTodos] = useState([{ text: '学习 Hook' }]);
+```
+
+在以上组件中,我们有局部变量 `age`,`fruit` 和 `todos`,并且我们可以单独更新它们:
+
+```jsx
+ function handleOrangeClick() {
+ // 和 this.setState({ fruit: 'orange' }) 类似
+ setFruit('orange');
+ }
+```
+
+你**不必**使用多个 state 变量。**State 变量可以很好地存储对象和数组,因此,你仍然可以将相关数据分为一组**。然而,不像 class 中的 `this.setState`,**更新 state 变量总是*替换*它而不是合并它**。
+
+我们[在 FAQ 中](https://zh-hans.reactjs.org/docs/hooks-faq.html#should-i-use-one-or-many-state-variables)提供了更多关于分离独立 state 变量的建议。
+
+
+
+> 把所有 state 都放在同一个 `useState` 调用中,或是每一个字段都对应一个 `useState` 调用,这两方式都能跑通。当你在这两个极端之间找到平衡,然后把相关 state 组合到几个独立的 state 变量时,组件就会更加的可读。如果 state 的逻辑开始变得复杂,我们推荐 [用 reducer 来管理它](https://zh-hans.reactjs.org/docs/hooks-reference.html#usereducer),或使用自定义 Hook。
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/《React》笔记/03.Hook/03.使用Effect Hook.md b/docs/《React》笔记/03.Hook/03.使用Effect Hook.md
new file mode 100644
index 0000000..3353585
--- /dev/null
+++ b/docs/《React》笔记/03.Hook/03.使用Effect Hook.md
@@ -0,0 +1,269 @@
+# 03. 使用Effect Hook(副作用钩子)
+
+如果你熟悉 React class 的生命周期函数,你可以把 `useEffect` Hook 看做 `componentDidMount`(挂载完成),`componentDidUpdate`(更新完成) 和 `componentWillUnmount`(即将销毁前) 这三个函数的组合。
+
+> Did : 做了... Will: 将要...
+
+
+
+**数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用**。不管你知不知道这些操作,或是“副作用”这个名字,应该都在组件中使用过它们。
+
+
+
+在 React 组件中有两种常见副作用操作:**需要清除的 **和 **不需要清除的**。我们来更仔细地看一下他们之间的区别。
+
+
+
+## 无需清除的 effect
+
+有时候,我们只想**在 React 更新 DOM 之后运行一些额外的代码。**比如发送网络请求,手动变更 DOM,记录日志,这些都是常见的无需清除的操作。因为我们在执行完这些操作之后,就可以忽略他们了。让我们对比一下使用 class 和 Hook 都是怎么实现这些副作用的。
+
+
+
+### 使用 class 的示例
+
+在 React 的 class 组件中,`render` 函数是不应该有任何副作用的。一般来说,在这里执行操作太早了,我们基本上都希望在 React 更新 DOM 之后才执行我们的操作。
+
+这就是为什么在 React class 中,我们把副作用操作放到 `componentDidMount` 和 `componentDidUpdate` 函数中。回到示例中,这是一个 React 计数器的 class 组件。它在 React 对 DOM 进行操作之后,立即更新了 document 的 title 属性
+
+```jsx
+class Example extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ count: 0
+ };
+ }
+
+ // DOM挂载了
+ componentDidMount() {
+ document.title = `You clicked ${this.state.count} times`;
+ }
+ // DOM更新了
+ componentDidUpdate() {
+ document.title = `You clicked ${this.state.count} times`;
+ }
+
+ render() {
+ return (
+
+
You clicked {this.state.count} times
+
+
+ );
+ }
+}
+```
+
+注意,**在这个 class 中,我们需要在两个生命周期函数中编写重复的代码。**
+
+这是因为很多情况下,我们希望在组件加载和更新时执行同样的操作。**从概念上说,我们希望它在每次渲染之后执行** —— 但 React 的 class 组件没有提供这样的方法。即使我们提取出一个方法,我们还是要在两个地方调用它。
+
+现在让我们来看看如何使用 `useEffect` 执行相同的操作。
+
+
+
+### 使用 Hook 的示例
+
+```jsx
+import React, { useState, useEffect } from 'react';
+function Example() {
+ const [count, setCount] = useState(0);
+
+
+ useEffect(() => { // 每次渲染组件DOM后执行此回调函数(即,挂载完成和更新完成生命周期)
+ document.title = `You clicked ${count} times`;
+ });
+ return (
+
+
You clicked {count} times
+
+
+ );
+}
+```
+
+**`useEffect` 做了什么?** 通过使用这个 Hook,你可以告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式的 API。
+
+**为什么在组件内部调用 `useEffect`?** 将 `useEffect` 放在组件内部让我们可以在 effect 中直接访问 `count` state 变量(或其他 props)。我们不需要特殊的 API 来读取它 —— 它已经保存在函数作用域中。Hook 使用了 JavaScript 的闭包机制,而不用在 JavaScript 已经提供了解决方案的情况下,还引入特定的 React API。
+
+**`useEffect` 会在每次渲染后都执行吗?** 是的,默认情况下,它在第一次渲染之后*和*每次更新之后都会执行。(我们稍后会谈到[如何控制它](https://zh-hans.reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects)。)你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。
+
+
+
+### 不同之处
+
+与 `componentDidMount` 或 `componentDidUpdate` 不同,使用 `useEffect` 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快。大多数情况下,effect 不需要同步地执行。在个别情况下(例如测量布局),有单独的 [`useLayoutEffect`](https://zh-hans.reactjs.org/docs/hooks-reference.html#uselayouteffect) Hook 供你使用,其 API 与 `useEffect` 相同。
+
+
+
+## 需要清除的 effect
+
+之前,我们研究了如何使用不需要清除的副作用,还有一些副作用是需要清除的。例如**订阅外部数据源**。这种情况下,清除工作是非常重要的,可以防止引起内存泄露!现在让我们来比较一下如何用 Class 和 Hook 来实现。
+
+
+
+### 使用 Class 的示例
+
+```jsx
+class FriendStatus extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { isOnline: null };
+ this.handleStatusChange = this.handleStatusChange.bind(this);
+ }
+
+ // 挂载后
+ componentDidMount() {
+ ChatAPI.subscribeToFriendStatus(
+ this.props.friend.id,
+ this.handleStatusChange
+ );
+ }
+
+ // 即将卸载
+ componentWillUnmount() {
+ ChatAPI.unsubscribeFromFriendStatus(
+ this.props.friend.id,
+ this.handleStatusChange
+ );
+ }
+ handleStatusChange(status) {
+ this.setState({
+ isOnline: status.isOnline
+ });
+ }
+
+ render() {
+ if (this.state.isOnline === null) {
+ return 'Loading...';
+ }
+ return this.state.isOnline ? 'Online' : 'Offline';
+ }
+}
+```
+
+
+
+### 使用 Hook 的示例
+
+```jsx
+import React, { useState, useEffect } from 'react';
+
+function FriendStatus(props) {
+ const [isOnline, setIsOnline] = useState(null);
+
+ useEffect(() => { // 每次渲染组件DOM后执行此回调函数(即,挂载完成和更新完成生命周期)
+ function handleStatusChange(status) {
+ setIsOnline(status.isOnline);
+ }
+ ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
+ // 每次重新渲染前都会执行(class的 componentWillUnmount 只在卸载组件执行)
+ return function cleanup() { // 函数名非必需,可使用箭头函数
+ ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
+ };
+ });
+
+ if (isOnline === null) {
+ return 'Loading...';
+ }
+ return isOnline ? 'Online' : 'Offline';
+}
+```
+
+
+
+## 使用 Effect 的提示
+
+### 使用Hook的目的
+
+使用 Hook 其中一个[目的](https://zh-hans.reactjs.org/docs/hooks-intro.html#complex-components-become-hard-to-understand)就是要解决 class 中生命周期函数经常包含不相关的逻辑,但又把相关逻辑分离到了几个不同方法中的问题。
+
+
+
+### 提示: 使用多个 Effect 实现关注点分离
+
+可以使用多个 effect。这会将不相关逻辑分离到不同的 effect 中:
+
+```jsx
+function FriendStatusWithCounter(props) {
+ const [count, setCount] = useState(0);
+ useEffect(() => {
+ document.title = `You clicked ${count} times`;
+ });
+
+ const [isOnline, setIsOnline] = useState(null);
+ useEffect(() => {
+ function handleStatusChange(status) {
+ setIsOnline(status.isOnline);
+ }
+
+ ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
+ return () => {
+ ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
+ };
+ });
+ // ...
+}
+```
+
+
+
+### 解释: 为什么每次更新的时候都要运行 Effect
+
+如果你已经习惯了使用 class,那么你或许会疑惑**为什么 effect 的清除阶段在每次重新渲染时都会执行,而不是只在卸载组件的时候执行一次**。让我们看一个实际的例子,看看为什么这个设计可以帮助我们创建 bug 更少的组件。
+
+...
+
+**忘记正确地处理 `componentDidUpdate` 是 React 应用中常见的 bug 来源**。
+
+...
+
+
+
+### 提示: 通过跳过 Effect 进行性能优化
+
+在某些情况下,每次渲染后都执行清理或者执行 effect 可能会导致性能问题。
+
+如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React **跳过**对 effect 的调用,只要传递数组作为 `useEffect` 的第二个可选参数即可:
+
+```jsx
+useEffect(() => {
+ document.title = `You clicked ${count} times`;
+}, [count]); // 仅在 count 更改时更新
+```
+
+对于有清除操作的 effect 同样适用:
+
+```jsx
+useEffect(() => {
+ function handleStatusChange(status) {
+ setIsOnline(status.isOnline);
+ }
+
+ ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
+ return () => {
+ ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
+ };
+}, [props.friend.id]); // 仅在 props.friend.id 发生变化时,重新订阅
+```
+
+未来版本,可能会在构建时自动添加第二个参数。
+
+
+
+**注意:**
+
+1. 如果你要使用此优化方式,请确保数组中包含了**所有外部作用域中会随时间变化并且在 effect 中使用的变量**,否则你的代码会引用到先前渲染中的旧变量。
+
+2. 如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组(`[]`)作为第二个参数。
+
+3. 如果你传入了一个空数组(`[]`),effect 内部的 props 和 state 就会一直拥有其初始值。尽管传入 `[]` 作为第二个参数更接近大家更熟悉的 `componentDidMount` 和 `componentWillUnmount` 思维模式,但我们有[更好的](https://zh-hans.reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies)[方式](https://zh-hans.reactjs.org/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often)来避免过于频繁的重复调用 effect。除此之外,请记得 React 会等待浏览器完成画面渲染之后才会延迟调用 `useEffect`,因此会使得额外操作很方便。
+
+4. 我们推荐启用 [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks#installation) 中的 [`exhaustive-deps`](https://github.com/facebook/react/issues/14920) 规则。此规则会在添加错误依赖时发出警告并给出修复建议。
+
+
\ No newline at end of file
diff --git a/docs/《React》笔记/03.Hook/04.Hook规则.md b/docs/《React》笔记/03.Hook/04.Hook规则.md
new file mode 100644
index 0000000..6702ea3
--- /dev/null
+++ b/docs/《React》笔记/03.Hook/04.Hook规则.md
@@ -0,0 +1,61 @@
+# 04. Hook规则
+
+Hook 本质就是 JavaScript 函数,但是在使用它时需要遵循**两条规则**。我们提供了一个 [linter 插件](https://www.npmjs.com/package/eslint-plugin-react-hooks)来强制执行这些规则:
+
+## 只在最顶层使用 Hook
+
+不要在循环,条件或嵌套函数中调用 Hook, 确保**总是在你的 React 函数的最顶层以及任何 return 之前调用他们**。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。
+
+:::tip
+
+1. React 怎么知道哪个 state 对应哪个 `useState`?答案是 React 靠的是 Hook 调用的顺序
+
+2. 如果我们想要有条件地执行一个 effect,可以将判断放到 Hook 的*内部*:
+
+```jsx
+ useEffect(function persistForm() {
+ // 👍 将条件判断放置在 effect 中
+ if (name !== '') {
+ localStorage.setItem('formData', name);
+ }
+ });
+```
+
+:::
+
+## 只在 React 函数中调用 Hook
+
+**不要在普通的 JavaScript 函数中调用 Hook。**你可以:
+
+- ✅ 在 React 的函数组件中调用 Hook
+- ✅ 在自定义 Hook 中调用其他 Hook
+
+
+
+
+
+## ESLint 插件
+
+我们发布了一个名为 [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks) 的 ESLint 插件来强制执行这两条规则。
+
+```sh
+npm install eslint-plugin-react-hooks --save-dev
+```
+
+
+
+```js
+// 你的 ESLint 配置
+{
+ "plugins": [
+ // ...
+ "react-hooks"
+ ],
+ "rules": {
+ // ...
+ "react-hooks/rules-of-hooks": "error", // 检查 Hook 的规则
+ "react-hooks/exhaustive-deps": "warn" // 检查 effect 的依赖
+ }
+}
+```
+
diff --git a/docs/《React》笔记/03.Hook/05.自定义 Hook.md b/docs/《React》笔记/03.Hook/05.自定义 Hook.md
new file mode 100644
index 0000000..a33185f
--- /dev/null
+++ b/docs/《React》笔记/03.Hook/05.自定义 Hook.md
@@ -0,0 +1,83 @@
+# 05. 自定义 Hook
+
+**通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。**
+
+目前为止,在 React 中有两种流行的方式来共享组件之间的状态逻辑: [render props](https://zh-hans.reactjs.org/docs/render-props.html) 和[高阶组件](https://zh-hans.reactjs.org/docs/higher-order-components.html),现在让我们来看看 Hook 是如何在让你不增加组件的情况下解决相同问题的。
+
+
+
+## 提取自定义 Hook
+
+**自定义 Hook 是一个函数,其名称以 “`use`” 开头,函数内部可以调用其他的 Hook。** 例如,下面的 `useFriendStatus` 是我们第一个自定义的 Hook:
+
+```jsx
+import { useState, useEffect } from 'react';
+
+function useFriendStatus(friendID) {
+ const [isOnline, setIsOnline] = useState(null);
+
+ useEffect(() => {
+ function handleStatusChange(status) {
+ setIsOnline(status.isOnline);
+ }
+
+ ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
+ return () => {
+ ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
+ };
+ });
+
+ return isOnline;
+}
+```
+
+与 React 组件不同的是,自定义 Hook 不需要具有特殊的标识。我们可以自由的决定它的参数是什么,以及它应该返回什么(如果需要的话)。换句话说,它**就像一个正常的函数**。但是它的名字应该始终以 `use` 开头,这样可以一眼看出其符合 [Hook 的规则](https://zh-hans.reactjs.org/docs/hooks-rules.html)。
+
+
+
+## 使用自定义 Hook
+
+现在我们已经把这个逻辑提取到 `useFriendStatus` 的自定义 Hook 中,然后就可以*使用它了:*
+
+```jsx
+function FriendStatus(props) {
+ const isOnline = useFriendStatus(props.friend.id);
+ if (isOnline === null) {
+ return 'Loading...';
+ }
+ return isOnline ? 'Online' : 'Offline';
+}
+```
+
+
+
+```jsx
+
+function FriendListItem(props) {
+ const isOnline = useFriendStatus(props.friend.id);
+ return (
+
+ {props.friend.name}
+
+ );
+}
+```
+
+**这段代码等价于原来的示例代码吗?**等价,它的工作方式完全一样。如果你仔细观察,你会发现我们没有对其行为做任何的改变,我们只是将两个函数之间一些共同的代码提取到单独的函数中。**自定义 Hook 是一种自然遵循 Hook 设计的约定,而并不是 React 的特性。**
+
+**自定义 Hook 必须以 “`use`” 开头吗?**必须如此。这个约定非常重要。不遵循的话,由于无法判断某个函数是否包含对其内部 Hook 的调用,React 将无法自动检查你的 Hook 是否违反了 [Hook 的规则](https://zh-hans.reactjs.org/docs/hooks-rules.html)。
+
+**在两个组件中使用相同的 Hook 会共享 state 吗?**不会。自定义 Hook 是一种重用*状态逻辑*的机制(例如设置为订阅并存储当前值),所以每次使用自定义 Hook 时,其中的所有 state 和副作用都是完全隔离的。
+
+**自定义 Hook 如何获取独立的 state?**每次*调用* Hook,它都会获取独立的 state。由于我们直接调用了 `useFriendStatus`,从 React 的角度来看,我们的组件只是调用了 `useState` 和 `useEffect`。 正如我们在[之前章节](https://zh-hans.reactjs.org/docs/hooks-effect.html#tip-use-multiple-effects-to-separate-concerns)中[了解到的](https://zh-hans.reactjs.org/docs/hooks-state.html#tip-using-multiple-state-variables)一样,我们可以在一个组件中多次调用 `useState` 和 `useEffect`,它们是完全独立的。
+
+###
+
+### 小总结
+
+1. 自定义hook就像一个普通函数,内部包含可复用的组件逻辑。
+2. 函数名以`use`开头。
+3. `state`是独立的。
+
+
+