本文大部分来自ConardLi 的 blog,建议直接看原博客
What & Why
使用 Hooks,你可以在将含有 state 的逻辑从组件中抽象出来,这将可以让这些逻辑容易被测试。同时,Hooks 可以帮助你在不重写组件结构的情况下复用这些逻辑。所以,它也可以作为一种实现状态逻辑复用的方案
解决了 HOC 嵌套地狱的问题,使得逻辑复用更加清晰
State Hook
useState 是一个钩子,他可以为函数式组件增加一些状态,并且提供改变这些状态的函数,同时它接收一个参数,这个参数作为状态的默认值
Effect Hook
Effect Hook 可以让你在函数组件中执行一些具有 side effect(副作用)的操作
参数
- 第一个参数是回调函数,在第组件一次 render 和之后的每次 update 后运行,React 保证在 DOM 已经更新完成之后才会运行回调;这个回调函数可以 return 一个函数,在执行下一个 useEffect 之前,会执行这个函数,常常用来对上一次调用 useEffect 进行清理,并且可以拿到上一次的状态(原理是闭包)
- 第二个参数是状态依赖
作用
模拟生命周期
- componentDidMount:状态依赖为空数组即可(其实官方并不推荐这种写法)
1 | function useDidMount(callback) { |
- componentWillUnmount:作为回调函数的 return 函数
1 | function useUnMount(callback) { |
ref Hook
用来获取 DOM 的 ref
1 | export default function Input() { |
注意 useRef()并不仅仅可以用来当作获取 ref 使用,使用 useRef 产生的 ref 的 current 属性是可变的,这意味着你可以用它来保存一个任意值
模拟 componentDidUpdate
componentDidUpdate 就相当于除去第一次调用的 useEffect,我们可以借助 useRef 生成一个标识,来记录是否为第一次执行:
1 | function useDidUpdate(callback, prop) { |
自定义 Hook
自定义 Hook 非常简单,我们只需要定义一个函数,并且把相应需要的状态和 effect 封装进去,同时,Hook 之间也是可以相互引用的。使用 use 开头命名自定义 Hook,这样可以方便 eslint 进行检查。
自定义 Hook 不需要具有特殊的标识。我们可以自由的决定它的参数是什么,以及它应该返回什么(如果需要的话)。换句话说,它就像一个正常的函数
使用 Hook 的动机
- 减少状态逻辑复用的风险
- 多个 Hook 之前不会相互影响,不会相互覆盖;Hook 也可以避免 HOC 使用不规范时的 props 覆盖
- 避免嵌套地狱
- 让组件更容易理解:能用 Hook 讲公共逻辑抽离成函数,将一个组件分割成更小的函数,而不是强制基于生命周期方法进行分割
- 函数代替 class
注意点
- useState 的 setXX 是异步函数,所以 setXXX 后立刻 console.log(XXX)的话是得不到的
- 在 setTimeout 这种闭包中中读不到其他状态的新值
- 记住 Hook 不能写在条件(if),循环(for),和嵌套函数中使用