React Hooks 的概念和意义

什么是 Hooks?

Hooks let you use state and other React features without writing a class

以往 react 的组件都是以 class 形式编写, 只有无状态组件才可以用函数来编写.
Hooks 就允许我们在函数组件中使用预定义的内部函数来标记状态和组件生命周期, 使得几乎所有组件都可以用函数来编写

以前类组件的不足
状态逻辑复用难

  • 缺少复用机制
  • 渲染属性和高阶组件导致层级冗余

趋向复杂难以维护

  • 生命周期函数混杂不相干逻辑
  • 相干逻辑分散在不同生命周期

this 指向困扰

  • 内联函数过度创建新句柄
  • 类成员函数不能保证 this

Hooks 优势

  • 函数组件无 this 问题
  • 自定义 Hook 方便复用状态逻辑
  • 副作用的关注点分离

使用 Hooks 函数

按照约定, 所有 hooks 函数包括自定义的, 都以 use 开头

1. 使用 useState

先看传统形式写组件写法:

import React, { Component } from 'react';

class App extends Component {
  state = {
    count: 0
  }
  render() {
    const { count } = this.state;
    return (
      <button type="button"
        onClick={() => {
          this.setState({ count: count + 1 })
        }}>

        Click({count})
      </button>
    )
  }
}

export default App;

Hooks 写法::

使用useState 返回值是一个具有两个成员的数组,
第一个是数据本身
第二个是设置数据的方法

有了第二个方法, 就不需要调用复杂的 setState 了

function App() {
  const [count, setCount] = useState(0);
  return (
    <button type="button"
      onClick={() => {
        setCount( count + 1 )
      }}>

      Click({count})
    </button>
  )
}

export default App;

这样就实现了一个与类组件等价的 hooks 组件
hooks 组件要比类组件更简洁明了

但目前还对组件中直接调用 useState 返回状态不太理解, useState 没有传入任何环境相关参数, 它如何知道要返回的是 count, 而且是知道哪个组件的 count.
可能原因:
首先 usestate 不知道它要返回的是 count, 但是它不需要知道它只要返回一个变量就行, 是使用者我们把它命名为 count 的, 我们也可以把它命名为任何名字.
第二 usestate 怎么知道返回的是当前组件的 count, 而不是其他组件 count, 因为 JS 是单线程的, 在 usestate 被调用时, 只可能在唯一组件上下文中. 利用全局唯一性推断


2 使用 Effect Hooks

上面的 useState 这个 hooks 函数, 是用来替代之前类组件中 state 成员和 setState 方法的解决方案. 但只有状态没法实现完整的业务流程. 在特定的状态, 节点和时机下都需要执行特定的行为, 这些行为置身于组件渲染过程之外, 比如绑定事件, 发起网络请求, 访问 dom 元素等等. 这些副作用的调用时机常在 mount (componentDidMount)之后, update(componentDidUpdate)之后, unmount(componentWillUnmount) 之前.
而现在用 useEffect 这样一个 hook函数覆盖所有情况.

为什么 useEffect 能够覆盖这么多场景? :

useEffect 标准上是在组件每次渲染之后调用, 并且根据自定义状态来决定调用还是不调用
第一次渲染后的调用就相当于 CDM, 后面的调用相当于 CDU
之前往往在这两个生命周期函数里编写一些相同的逻辑, 并不一定关心到底是 mount 还是 update
现在换做 useEffect 就减少了一份代码

CWU 呢
useEffect 的调用不仅仅代表一个函数的执行, 它还可以返回另一个回调函数.
这个函数的执行时机很重要, 它和 useEffect 的调用时机挂钩, 这个回调函数的作用: 清除上一次副作用遗留下的状态, 比如一个组件在第3 次, 第 5 次和第 7 次渲染后执行的 useEffect 逻辑, 那么回调函数就会在第4 次, 第 6 次, 第 8 次渲染前执行.
如果 useEffect 只在第一次调用, 那么它返回的回调函数, 就只会在组件卸载前调用了. 也就相当于 componetWillUnmount

useEffect第二个参数是一个数组, 只有在数组每一项都不变的情况下, useEffect 才不会执行.
第一次渲染后 useEffect 肯定执行, 因为没有初始值.但下一次什么时候再执行, 取决于数组每一项的对比
如果不传数组, 意味着每次渲染后都执行 useEffect.
如果传空数组, 空数组和空数组相同的, 因此 useEffect 只会在第一次执行一次.

无论之前生命周期函数, 还是现在的 useEffect 都是处理副作用的, 之前的生命周期函数在命名上更容易理解, 但其实也是围绕组件的渲染和重渲染的, useEffect 把他们都抽象了一层, 通过第二个参数来控制执行的时机, 与生命周期是等价的


3 使用 Context Hooks

回忆 Context:
允许数据跨越组件层级直接传递. 顶层声明 Provider 组件, 并声明 value 属性, 也就是 Context 的负载. 然后后代组件中声明 Consumer 组件,这个组件的子组件只能是唯一的一个函数, 函数参数即 Context 的负载, 如果有多个 Context, 那么 Provider 和 Consumer 就可以嵌套.
可以使用 ContextType 来简化负载的获取, 但在一个组件中即使要消费多个 Context, ContextType 也只能指向一个.

在 Hooks 环境中, Consumer 的方式依旧可以使用, 但是 ContextType 无法使用, 因为没有类组件.

function Counter() {
  const count = useContext(CountContext);
  return (
  <h1>{count}</h1>
  )
}

4 使用 Memo / CallBack Hooks

memo 用来优化函数组件的重渲染行为, 传入属性值都不变的情况下, 不会触发组件的重渲染
和类组件的 PureComponent 功能类似, 在 hooks 环境下, 几乎所有组件都是函数组件. 使用 memo 的几率要比 PC 高.

memo 函数针对的是一个组件的渲染是否重复执行
usememo 定义了一段函数逻辑是否重复执行

本质都是通过同样的算法 判定依赖是否发生改变, 进而决定是否触发特定逻辑. 比如输入输出是对等的, 相同的输入一定产生相同的输出, 利用 memo 可以避免不必要的重复计算, 减少资源浪费.

  useMemo(() => {}, []);

usememo 语法和 useeffect 一致
第一个参数是要执行的逻辑函数
第二个参数是这个逻辑依赖的变量组成的数组

如果不传第二个参数, 每次都会运行 usememo 的逻辑, 
那么 usememo 的意义就不存在了, 所以不要这么写

如果传入空数组, 就只会运行一次.

策略和 useEffect 一样
但有一点和它不同, 就是调用时机
useEffect 一定在渲染之后运行, 但 usememo 是需要有返回值, 
而返回值可以直接参与渲染, 因此 usememo 是在渲染期间完成的.
所以是一前一后的区别

useCallBack

如果 usememo 返回的是一个函数, 那么等价于 useCallBack,
因此 useCallback 算是 usememo 的变种.

使用 usecallback 可能依然创建新的函数, 但这个函数并不一定会被返回, 很可能创建的函数就被抛弃不用了, usecallback 解决的是传入子组件的参数过多变化导致子组件过度渲染的问题


4 使用 Ref Hooks
Ref 之前专门用来获取子组件或者 DOM 元素的句柄的.

useRef 作用:
获取子组件或者 DOM 节点的句柄
渲染周期之间共享数据的存储


5 自定义 Hooks


使用 Hooks 的使用法则

  1. 仅在顶层调用 Hooks 函数:
    不能在循环语句, 条件语句, 或者嵌套函数中调用 Hooks 函数, 因为整个 Hooks 函数很可能依赖调用顺序, 这样 react 才能在组件不同的渲染周期中, 把相同的逻辑关联起来, 一旦 hooks 函数不在顶层调用 那么很有可能在组件的不同渲染周期中, 他们调用顺序发生变化, 进而导致各种 bug.

  2. 仅在函数组件和自定义 hooks 函数中调用 hooks 函数
    不能在其他普通函数中调用


Hooks 常见问题

  1. 对传统 React 编程的影响
  • 生命周期函数如何映射到 Hooks?
    在这里插入图片描述
    constructor 一般就只在这里面初始化 state, 有时为了简单甚至直接用类属性而非 constructor 初始化 state. 现在在函数组件中我们直接用 usestate 来初始化 state, 也就不需要用 constructor 了.
    getDerivedStateFromProps 这是一个静态方法, 可以根据新的属性值和当前状态来返回新的状态, 一般用作根据传入属性的变化来调整状态上的场景, 就像名字. 在函数组件中, 可以在函数中获取完 state 后, 立即执行比较操作, 并根据需要来执行 setstate.
    render 函数组件本身就返回 jsx, 就包含了 render 能力
    componentDidMount
    componentDidUpdate
    componentWillUnmount
  1. 类实例成员变量如何映射到 Hooks?
  2. Hooks 中如何获取历史 props 和 state?
  3. 如何强制更新一个 Hooks 组件?
  • 主动创建一个不参与实际渲染 state, 然后更新它的值 以此来实现强制重新渲染
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值