抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

本文大部分来自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
2
3
function useDidMount(callback) {
useEffect(callback, [])
}
  • componentWillUnmount:作为回调函数的 return 函数
1
2
3
function useUnMount(callback) {
useEffect(() => callback, [])
}

ref Hook

用来获取 DOM 的 ref

1
2
3
4
5
6
7
8
9
10
11
12
export default function Input() {
const inputEl = useRef(null)
const onButtonClick = () => {
inputEl.current.focus()
}
return (
<div>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</div>
)
}

注意 useRef()并不仅仅可以用来当作获取 ref 使用,使用 useRef 产生的 ref 的 current 属性是可变的,这意味着你可以用它来保存一个任意值

模拟 componentDidUpdate

componentDidUpdate 就相当于除去第一次调用的 useEffect,我们可以借助 useRef 生成一个标识,来记录是否为第一次执行:

1
2
3
4
5
6
7
8
9
10
function useDidUpdate(callback, prop) {
const init = useRef(true)
useEffect(() => {
if (init.current) {
init.current = false
} else {
return 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),和嵌套函数中使用

参考资料
ConardLi 的 blog
官方文档

评论