什么是 Hooks?
Hooks 是一个 React 函数组件内一类特殊的函数(通常以 “use” 开头,比如 “useState”),使开发者能够在 function component 里依旧使用 state 和 life-cycles,以及使用 custom hook 复用业务逻辑。
动机
在 React 里,function component 就是一个 pure render component,没有 state 和 component life-cycle。如果需要这两个中的任意一个,就需要变成 class component。在既有的 React API 下,这个模式有如下一些缺点:
组件间交流的耦合度很高,组件树臃肿
在既有的模式下,React 的组件间通信无非是两种,一种是单项数据流,另一种是通过 redux 之类的 global store 来实现全局状态和各组件间的解耦。当有些状态不适合放在 global store 的情况下,组件间逻辑的复用和沟通就变得十分困难(必须一层一层往下传)。这一点在 Higher order component (高阶组件) 和 render props 中尤其常见。我们为了复用一些逻辑,单独创造了很多 HOC(高阶组件) 来向下传递状态。这导致的问题就是当我们的应用规模变得越来越大的时候,一些无关 UI 的 wrapper 组件越来越多,React 组件树变得越来越臃肿(在 devtool 中可以甚至看到数十层 wrapper)。某些业务场景下,一个 Tooltip component 里面都嵌套了三四层额外的组件,使开发和调试的效率变得很低。
在新的 React hook 中,我们可以创建 custom hook,在其中复用一些逻辑,这些逻辑不再出现在组件树中,而是成为一个单独的,独立的逻辑单元,但是他们仍然响应 React 在渲染之间的变化。
JavaScript 的 class 产生的诸多疑惑
这一点是相对于 JavaScript 来说的。还记得刚入门 JavaScript 的时候,需要跨越的一个重要难关就是 ”this” 指向,以及原型链,继承这些问题。即使我们真正觉得明白了其中的原理,在日常的开发中也难免因为疏忽而踩坑,这一系列的问题导致新手相对比较难上手 React。举个简单的例子,React 组件内的 event listener 之前需要手动 bind this 的问题,这个问题就很难对一个 JavaScript 入门的新手解释明白。
而这一系列的问题,将在 Hook 中被极大地解决。如果没有 class,没有了 this,可能上述的种种问题都不再是问题了。
Write Hooks
说了这么多,Hooks API 是什么样呢?首先需要声明的是,Hooks 是向后兼容的,class component 不会被移除。作为开发者,可以慢慢迁移到这个新的 API。
Hooks 主要分为三种:
● State hooks (在 function component 中使用 state)● Effect hooks (在 function component 中使用生命周期和 side effect)
● Custom hooks (自定义 hooks 用来复用组件逻辑,解决了上述的第一个动机中阐述的问题,这一部分就不在此多费篇幅介绍了,请大家移步文档)。
State hooks
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
之前讲过 hook 本质是一个特殊的函数(通常以 “use” 开头)。在这里,”useState” 就是一个 hook,通过它我们能够嵌入组件内部的 state。这个函数返回一个 pair,第一个值是当前对应这个 hook 的 state 值,第二个是怎样更新这个值。
我们可以从中感觉到,这两个返回值分别对应的以前的用法是:
● this.state● this.setState
除此之外,我们还可以在一个函数组件中使用多个 useState:
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}
这给我们的一个非常大的好处就是我们能够避免组件的 state 结构过于臃肿(因为之前每个 component class 只能有一个 state),能够独立处理每个 state。另一个好处就是这种写法非常直观,一眼就可以看出和这个 state 相关的两个变量,比如 [age, setAge]。
Effect hooks
import { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
我们还需要解决一个问题,那就是怎样在 function component 里使用 life-cycles,生命周期函数。在这里,所有的 life-cycles,比如 componentDidMount, componentDidUpdate, shouldUpdate, 等等都集合成一个 Hook,叫做 useEffect。这个函数类似 redux 中的 subscribe,每当 React 因为 state 或是 props 而重新 render 的之后,就会触发 useEffect 里的这个 callback listener(在第一次 render 和每次 update 后触发)。
为什么叫 useEffect 呢?因为我们通常在生命周期内做的操作很多都会产生一些 side-effect(副作用)的操作,比如更新 DOM,fetch 数据,等等。
Other Built-in Hooks
除了 useState, useEffect 还有另外一些 React 自带的 hooks。比如:
● useContext替代了 <Context.Consumer> 使用 render props 的写法,使组件树更加简洁。
● useReducer相当于组件自带的 redux reducer,负责接收 dispatch 分发的 action 并更新 state。
详细用法请看文档。
总结
读到这里,你可能理解了为什么这个新的 API 被叫做 “Hooks” 了。”Hooks” 本意是”钩子“的意思。在 React 里,hooks 就是一系列特殊的函数,使函数组件 (functional component) 内部能够”钩住“ React 内部的 state 和 life-cycles。
这个向后兼容的 API 在解决了一些既有问题的情况下,不仅使我们能够更好地使用 state 和 life-cycles,真正功能强大的地方是使我们能够更轻松地复用组件逻辑(custom hooks)。但是限于篇幅,很多功能强大的部分和一些注意事项在这篇文章里并没有过多讲解,请大家移步官方文档学习更详细的姿势(墙裂推荐)。
原文发布时间为:2018-11-02
本文作者:cyan
本文来自云栖社区合作伙伴“前端大学”,了解相关信息可以关注“前端大学”。