什么是 Hooks
- 凡是以 use 开头的 React API 都是 Hooks
- Hooks 是 React 16.8 新增的特性,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
- React 一直都提倡使用函数组件,但是有时候需要使用 state 或者其他一些功能时,只能使用类组件,因为函数组件没有实例,没有生命周期函数,只有类组件才有
- 如果你在编写函数组件并意识到需要向其添加一些 state,以前的做法是必须将其它转化为 class,现在你可以直接在现有的函数组件中使用 Hooks
Hooks 解决的问题
1. 类组件的不足
- 状态逻辑难复用:在组件之间复用状态逻辑很难,可能要用到 render props (渲染属性)或者 HOC(高阶组件),但无论是渲染属性,还是高阶组件,都会在原先的组件外包裹一层父容器(一般都是 div 元素),导致层级冗余
- 类定义更为复杂:
- 不同的生命周期会使逻辑变得分散且混乱,不易维护和管理(如:在
componentDidMount中注册事件以及其他的逻辑,在componentWillUnmount中卸载事件,这样分散不集中的写法,很容易写出 bug ) - 时刻需要关注
this的指向问题 - 复用代价高,类组件中到处都是对状态的访问和处理,导致组件难以拆分成更小的组件
- 不同的生命周期会使逻辑变得分散且混乱,不易维护和管理(如:在
2. Hooks 优势
- 能优化类组件以上的三大问题
- 状态与UI隔离:能在无需修改组件结构的情况下复用状态逻辑(自定义 Hooks )
- 能将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)
- 副作用的关注点分离:副作用指那些没有发生在数据向视图转换过程中的逻辑,如 ajax 请求、访问原生 dom 元素、本地持久化缓存、绑定/解绑事件、添加订阅、设置定时器、记录日志等。以往这些副作用都是写在类组件生命周期函数中的,而 useEffect 在全部渲染完毕后才会执行
注意事项
- 只能在函数内部的最外层调用 Hook,避免在 循环/条件判断/嵌套函数 中调用 Hooks,保证调用顺序的稳定
- 只有 函数定义组件 和 Hooks 可以调用 Hooks,避免在 类组件 或者 普通函数 中调用
- 不能在
useEffect中使用useState,React 会报错提示 - 类组件不会被替换或废弃,不需要强制改造类组件,两种方式能并存
常用的内置钩子
useState:用于定义函数组件的 state,起到类组件中this.state的功能useEffect:处理函数组件中的副作用,比如 ajax 请求、访问原生 DOM 节点等useReducer: 类似于 Redux 思想的实现,但其并不足以替代 Redux,可以理解成一个组件内部的 Redux:- 并不是持久化存储,会随着组件被销毁而销毁
- 属于组件内部,各个组件是相互隔离的,单纯用它无法共享数据
- 配合
useContext的全局性,可以完成一个轻量级的 Redux,达到数据共享
useCallback:缓存回调函数,避免传入的回调每次都是新的函数实例而导致依赖组件重新渲染,具有性能优化的效果useMemo:用于缓存传入的 props,避免依赖的组件每次都重新渲染,具有性能优化的效果useRef:获取组件的真实节点useContext:获取 context 对象useLayoutEffect- DOM 更新同步钩子,用法与
useEffect类似,只是区别于执行时间点的不同 useEffect属于异步执行,并不会等待 DOM 真正渲染后执行,而useLayoutEffect则会真正渲染后才触发- 可以获取更新后的 state
- DOM 更新同步钩子,用法与
自定义钩子
自定义钩子(useXxxxx): 基于 Hooks 可以引用其它 Hooks 的特性,我们可以编写自定义钩子,比如我们需要每个页面自定义标题:
function useTitle(title) {
useEffect(
() => {
document.title = title;
});
}
// 使用
function Home() {
......
const title = '我是首页'
useTitle(title)
......
}