Hook 简介
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
Hook 对 React 意味着什么?
- 完全可选的。 你无需重写任何已有代码就可以在一些组件中尝试 Hook。但是如果你不想,你不必现在就去学习或使用 Hook
- 100% 向后兼容的。 Hook 不包含任何破坏性改动
- 现在可用。 Hook 已发布于 v16.8.0
没有计划从 React 中移除 class 。Hook 不会影响你对 React 概念的理解。 恰恰相反,Hook 为已知的 React 概念提供了更直接的 API:props, state,context,refs 以及生命周期。
Hook 的使用动机是什么?
在使用 React 中,或许会遇见过以下的窘境
1、在组件之间复用状态逻辑很难
React 没有提供将可复用性行为“附加”到组件的途径(例如,把组件连接到 store)。render props 和 高阶组件 或许可以解决此类问题,但是这类方案需要重新组织组件结构,这可能会很麻烦,使代码难以理解。由 providers,consumers,高阶组件,render props 等其他抽象层组成的组件会形成“嵌套地狱”。
可以使用 Hook 从组件中提取状态逻辑,使得这些逻辑可以单独测试并复用。Hook 使你在无需修改组件结构的情况下复用状态逻辑。 这使得在组件间或社区内共享 Hook 变得更便捷。
2、复杂组件变得难以理解
组件起初很简单,但是逐渐会被状态逻辑和副作用充斥。每个生命周期常常包含一些不相关的逻辑。例如,组件常常在
componentDidMount
和
componentDidUpdate
中获取数据。但是,同一个
componentDidMount
中可能也包含很多其它的逻辑,如设置事件监听,而之后需在
componentWillUnmount
中清除。相互关联且需要对照修改的代码被进行了拆分,而完全不相关的代码却在同一个方法中组合在一起。如此很容易产生 bug,并且导致逻辑不一致。
在多数情况下,不可能将组件拆分为更小的粒度,因为状态逻辑无处不在。这也给测试带来了一定挑战。同时,这也是很多人将 React 与状态管理库结合使用的原因之一。但是,这往往会引入了很多抽象概念,需要你在不同的文件之间来回切换,使得复用变得更加困难。
为了解决这个问题,Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据),而并非强制按照生命周期划分,可以使用 reducer 来管理组件的内部状态,使其更加可预测。
3、难以理解的 class
除了代码复用和代码管理会遇到困难外,学习class 是 React 的一大屏障,理解 JavaScript 中
this
的工作方式,与其他语言存在巨大差异。还不能忘记绑定事件处理器。没有稳定的语法提案,这些代码非常冗余,或许你理解 props,state 和自顶向下的数据流,但对 class 却一筹莫展。
使用 class 组件会无意中鼓励开发者使用一些让优化措施无效的方案。class 也给目前的工具带来了一些问题。例如,class 不能很好的压缩,并且会使热重载出现不稳定的情况。
为了解决这些问题,Hook 使你在非 class 的情况下可以使用更多的 React 特性。 从概念上讲,React 组件一直更像是函数。而 Hook 则拥抱了函数,同时也没有牺牲 React 的精神原则。Hook 提供了问题的解决方案,无需学习复杂的函数式或响应式编程技术。
以上就是 React Hook 的一些简要介绍,接下来详细介绍 Hook 的其中之一:State Hook。
State Hook
基本例子
如下例子,在 React 中,使用 class 的方式创建一个计时器
class Count extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
state 初始值为
{ count: 0 }
,当用户点击按钮后,我们通过调用
this.setState()
来增加
state.count
。
之前,我们在创建具有状态的组件时,使用的是 class 的方式;而 Hook 可以另函数组件拥有更多的 React 特性,如 state 。使用 Hook 改写上面的 class 组件创建方式
import React, { useState } from 'react';
function Count() {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Hook 和 函数组件
React 的函数组件
const Count = (props) => {
// 你可以在这使用 Hook
return <div />;
}
或是这样
function Count(props) {
// 你可以在这使用 Hook
return <div />;
}
之前可能把它们叫做“无状态组件”,但现在为它们引入了使用 React state 的能力,所以更喜欢称之为”函数组件”。
看到这里,此处 Hook 到底是什么?
Hook 是什么?
在上面的示例中,引入 React 中
useState
的 Hook
import React, { useState } from 'react';
function Count() {
// ...
}
Hook 是一个特殊的函数,它可以让你“钩入” React 的特性。例如,
useState
是允许你在 React 函数组件中添加 state 的 Hook。 如果在编写函数组件并意识到需要向其添加一些 state,以前的做法是必须将其它转化为 class,现在可以在现有的函数组件中使用 Hook。
声明 State
使用 class 方式,在构造函数中设置
this.state
为
{ count: 0 }
来初始化
count
state 为
0
class Count extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
}
在函数组件中,没有
this
,所以不能分配或读取
this.state
。我们直接在组件中调用
useState
Hook
import React, { useState } from 'react';
function Count() {
// 声明一个叫 “count” 的 state 变量
const [count, setCount] = useState(0);
}
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
类似,唯一区别就是你需要成对的获取它们。
重看以下例子
import React, { useState } from 'react';
function Count() {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);
}
声明了一个叫
count
的 state 变量,然后把它设为
0
。React 会在重复渲染时记住它当前的值,并且提供最新的值给我们的函数。我们可以通过调用
setCount
来更新当前的
count
。
读取 State
在 class 中显示当前的 count,我们读取
this.state.count
<p>You clicked {this.state.count} times</p>
在函数中,我们可以直接用
count
<p>You clicked {count} times</p>
更新 State
在 class 中,我们需要调用
this.setState()
来更新
count
值
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
在函数中,我们使用
setCount
更新
count
<button onClick={() => setCount(count + 1)}>
Click me
</button>
示例分析
步骤分析
// 引入 React 中的 useState Hook。它让我们在函数组件中存储内部 state。
import React, { useState } from 'react';
function Count() {
// 在 Count 组件内部,我们通过调用 useState Hook 声明了一个新的 state 变量。
// 它返回一对值给到我们命名的变量上。我们把变量命名为 count,因为它存储的是点击次数。
// 我们通过传 0 作为 useState 唯一的参数来将其初始化为 0。第二个返回的值本身就是一个函数。
// 它让我们可以更新 count 的值,所以我们叫它 setCount。
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<!-- 当用户点击按钮后,我们传递一个新的值给 setCount。React 会重新渲染 Count 组件,并把最新的 count 传给它。 -->
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
方括号的作用
const [count, setCount] = useState(0);
以上是 JavaScript 语法叫数组解构。它意味着我们同时创建了 count 和
setCount
两个变量,count 的值为
useState
返回的第一个值,
setCount
是返回的第二个值。它等价于下面的代码
const countStateVariable = useState(0); // 返回一个有两个元素的数组
const count = countStateVariable[0]; // 数组里的第一个值
const setCount = countStateVariable[1]; // 数组里的第二个值
当我们使用
useState
定义 state 变量时候,它返回一个有两个值的数组。第一个值是当前的 state,第二个值是更新 state 的函数。使用
[0]
和
[1]
来访问有点令人困惑,因为它们有特定的含义。这就是我们使用数组解构的原因。
使用多个 state 变量
将 state 变量声明为一对
[something, setSomething]
也很方便,因为如果我们想使用多个 state 变量,它允许我们给不同的 state 变量取不同的名称
function ManyStates() {
// 声明多个 state 变量
const [age, setAge] = useState(18);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: '学习 Hook' }]);
}
在以上组件中,我们有局部变量
age
,
fruit
和
todos
,并且我们可以单独更新它们
function handleAgeClick() {
// 和 this.setState({ age: 19 }) 类似
setAge(19);
}
总结
以上内容中,我们了解了 React 提供的
useState
Hook,有时候我们也叫它 “State Hook”。它让我们在 React 函数组件上添加内部 state 。
我们还学到了什么是 Hook。Hook 是能让你在函数组件中“钩入” React 特性的函数。它们名字通常都以
use
开始,不妨试试在项目中使用。
(更多前端相关博客请查看我的主页喔~~~关注我,一起学前端,共同进步)
版权归原作者 前端不释卷leo 所有, 如有侵权,请联系我们删除。