Hooks 详解

Hooks

React Hook/Hooks

  • Hook 是 React 16.8 版本后新增的特性/新语法
  • 可以让函数式组件使用 state 以及其他 React 特性

注意点

不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的  useState  和  useEffect  调用之间保持 hook 状态的正确。

因为 react 是通过 hook 调用顺序(index) 来判断得。条件判断和循环会打乱原有顺序,产生 bug


State Hook

  • 解构赋值一个数组,第一个参数是状态,第二个参数是更新状态的方法,useState 的参数是该状态的初始值:普通类型,对象以及 class
  • 状态更新的两种方式,推荐第二种,但 state 为对象,更常用第一种。
    • 但是在 setInterval 中使用 setState,由于setInterval的闭包特性,每次组件render setInterval回调中的state被闭包了导致不会改变,所以此种情况用回调的更新方式可以解决这个问题``或使用 useRef。**所以应该减少在闭包中使用外部普通变量,尤其是 state。**
  • 函数组件中不使用 this,直接调用 state 或方法即可。

注意 class 组件中 this.setState 更新是 state 是合并, setState 中只用传需更新有变化的 state, 而useState 中 setState 是替换,也就是说 useState 中必须传递整个 state

所以这样写<font style="color:#23263B;"> {...未更新state, 更新的state = value}</font>

需要另外注意的是:不少函数指令式的组件 比如 antd 的 <font style="color:#23263B;">Modal.</font>confirm() 中不应该使用组件内的 state, 因为 state 如何更新 render, 调用 <font style="color:#23263B;">Modal.</font>confirm 的函数也只会执行一次,所以无法引起 modal 内部更新刷新。如果想往 modal 中注入 state 只有使用 <font style="color:#23263B;"><Modal/> 组件</font>


Effect Hook

Effect Hook  可以让你在函数组件中执行副作用操作(就是模拟类式组件中的生命周期钩子)

useEffect Hook 就相当于以下三种钩子的组合:

componentDidMount()、componentDidUpdate()、componentWillUnmount()

  • React 中的副作用操作多有:
    • 发送 ajax 请求
    • 设置订阅/启动定时器
    • 手动更改真实 DOM

语法:

effect 在每次渲染的时候都会执行。并且 React**  会**在执行当前 effect 之前对上一个 effect 进行清除。

在需要清除副作用的业务场景中(定时器,异步订阅等)。class 组件要做到这一点需要在 componentDidUpdate 中,手动对更新前的副作用进行清除,然后再订阅新的内容。显然 useEffect 就不用这么麻烦了。

  • useEffect 有两个参数第一个参数是一个函数,可以在里面写副作用操作,并可在函数的 return 函数里进行一些清除操作(清除定时器/异步订阅等),该清除函数会在执行当前 effect 之前,对前一个 effect 进行清除。
  • 不写**useEffect 的**第二参数第一次渲染之后和每次更新之后都会执行,也就是不管状态更新还是初始化,都会执行 useEffect 中的代码。
  • useEffect 第二参数为一个数组且数组为空,相当于componentDidMount。其 return 函数就有componentWillUnmount__的效果。
    • 第二个参数中,写上依赖的变量,即可有 componentDidMount()、和对应状态的 componentDidUpdate() 、等两个钩子的效果。_并且需要注意 处理清除事务的 return 函数,只有在第二个参数为空数组时才算挂载前钩子_
    • useEffect 也是一个性能优化的案例,第二个参数,所依赖的值,只有在发生变化时。才会执行 Effect 回调。

**Tip: **


  • React 会等待浏览器完成画面渲染之后才会延迟调用 useEffect,因此会使得额外操作很方便。就是说useEffect 是在 DOM 更新后执行的,所以能拿到,最新的 state;
  • useEffect** **与其他存在依赖值的 hook, 比如 useMemo、useCallback。依赖参数的规则一致;
  • 可使用多个 useEffect,关注分离的处理不同事务逻辑,react 会按顺序依次执行。

Ref Hook

useRef

const params = useRef(初始值)

TS 中 向组件上注入 ref 时需声明 绑定 ref 组件的泛型,就是你 ref 的类型。获取 dom 节点使用 HTMLElement 类型,单纯使用 useRef 存储变量时不用。

useRef<types>(initVal)

在页面整个生命周期中,除了挂载时,ref 会用于页面渲染,之后任何变化都不会引起页面的渲染。

一、useRef 的应用场景:

1.函数组件访问 dom 元素。

2.函数组件整个生命周期里存储变量。

二、useRef 特性:

1.current 中保存的属性 类似 class 组件中的实例属性, 就像一个组件内的全局变量一样.

2.ref.current 发生变化并不会造成 re-render; 会在组件整生命周期里保存。

3.不用于组件渲染的属性,都应该存到 useRef 中。

4.单纯维护变量在 useRef 中,就不必绑定在 dom 节点上了。

5.不应该在 render 阶段更新 current 属性ref.current 的变化,不应该导致页面更新等副作用

6.不使用 setState 更新 state,直接赋值,能到达和 useRef 一样的效果<不推荐>

三、useRe 当作普遍 ref 使用:

–创建

–标记

–使用

四、不同

类组件的 creatRef 会重复渲染。而 useRef 不会


useCallback 与 useMemo

性能优化与缓存数据。(类似计算属性,class 组件中 使用 get 关键字命名方法也算计算属性)


> useMemo 和 useCallback 的共同点:

  • 接收的参数都是一样的,第一个是回调函数,第二个是依赖的数据
  • 它们都是当依赖的数据发生变化时才会重新计算结果,起到了缓存作用

> useMemo 和 useCallback 的区别:

  • useMemo 计算结果是 return 回来的值,通常用于缓存计算结果的值
  • useCallback 计算结果是一个函数,通常用于缓存函数
    • 常使用 useCallback(() => callback(), [])缓存匿名函数,(为了避免匿名函数被重复调用的性能问题,暂未知)

> 可以把 useMemo 替换成 useCallback,使用 useCallback 就不用写 return 函数了

1
2
3
4
5
6
7
8
9
const onClickChild = useMemo(() => {
return () => {
console.log(m);
};
}, [m]);
//等价于
const onClickChild = useCallback(() => {
console.log(m);
}, [m]);

注意:

  • 如果你的 value 是个函数,那么你就要写成useMemo(()=>(x)=> console.log(x))
  • 这是一个返回函数的函数,比较复杂;就使用useCallback

useMemo 和 React.memo

memo 是防止 props 没变时的重新渲染,useMemo 和 useCallback 是防止 props 的不必要变化。

hook 中的性能优化主要有 useMemo 和 React.memo。其中 React.memo 在没有第二的参数的时候相当于 class 中的 PureComponent,当增加了第二个参数的时候相当于声明周期中的 shouldComponentUpdate,但是可以明显感觉到 hook 的使用要更加的简单和灵活。

useMemo 则是 hook 对比 class 一个显著的优势,因为通过 useMemo 我们可以进行更加细粒度的性能优化,这是 class 难以实现的。

自定义 hook

  • 本质就是抽离逻辑和状态更新。公司里写得 .store.ts 文件,就是自定义 hook.
  • 且约定 使用 use 开头。

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!