Hook 是 React 16.8 的新增特性,它可以让我们在不使用 class 的情况下使用 state 和 React 的其他特性。

# 知识回顾

  在 react 中我们就已经提到了,函数式组件的缺点:

缺点 描述
没有内部状态 class 组件可以定义 state 来保存自己的内部状态,而函数式组件中函数每次调用会产生新的临时变量
没有生命周期 class 组件可以在特定周期执行特殊的操作(如:发送网络请求),而函数式组件中函数每次调用都会重新发送网络请求
没有组件实例 意味着不会有 class 组件中的 this 指向问题

  除此之外,我们在 React 核心中还接触到了:

hooks 作用
memo() 和 PureComponents 功能类似
useRef() + forwardRef() 获取 DOM 元素

# 1、useState

类数组实现
// 使用 PureComponent
class Demo extends React.PureComponent {
  constructor(props) {
    super(props)

    this.state = {
      count: 1
    }
  }

  countChange() {
    this.setState({
      count: this.state.count + 1
    })
  }

  render() {
    const { count } = this.state

    return (
      <div>
        <p>计数:{count}</p>
        <button onClick={(e) => this.countChange()}>count值 +1</button>
      </div>
    )
  }
}

提示

  hook 只能在函数组件内的顶层调用

  hook 不能在类组件、普通函数等中调用

import { memo, useState } from 'react'

function Demo(props) {
  // 数组解构
  let [count, setCount] = useState(1)

  function countChange() {
    setCount(count + 1)
  }

  return (
    <div>
      <p>计数:{count}</p>
      <button onClick={countChange}>count值 +1</button>
    </div>
  )
}

// 使用 memo
export default memo(Demo)

# 2、useEffect

  Effect Hook 可以让我们完成一些在类组件中的生命周期中使用的功能(副作用操作):

网络请求、DOM 操作、事件监听等

提示

  -一个函数式组件中是可以有多个 Effect 的,所以当 Effect 中内容过多时,我们可以将它们拆分出来

vue 实现

  利用 vue-router 的导航守卫

// 当前组件中
router.afterEach((to, from) => {
  document.title = to.meta.title || 'lencamo'
})
类组件实现
// 待跳转组件中
class Demo extends React.PureComponent {
  componentDidMount() {
    document.title = this.state.pageTitle
  }

  // componentDidUpdated() {
  //   document.title = this.state.pageTitle
  // }
}
import { useState, useEffect } from 'react'

function Demo(props) {
  let [pageTitle, setPageTitle] = useState('分类')

  // 当前组件发生渲染时自动执行🚩
  useEffect(() => {
    //
    document.title = pageTitle
  })

  return <div></div>
}

# 3、清除 Effect

  其实,我们可以把 useEffect Hook 看做 componentDidMountcomponentDidUpdatecomponentWillUnmount 这三个函数的组合。

  在类组件中我们可以使用componentDidMountcomponentWillUnmount来进行订阅和取消订阅,那在函数式组件中如果实现呢?

关于下面的 cleanup 函数名是方便阅读而命名的,可以不用命名返回一个函数就可以了

function Demo(props) {
  useEffect(() => {
    console.log('组件被创建了')
    const unsubscribe = store.subscribe(() => console.log(store.getState())) // 启用

    return function cleanup() {
      unsubscribe() // 取消
      console.log('组件被销毁了')
    }
  })

  return <div></div>
}

# 4、Effect 性能优化

  默认情况下,每当函数式组件重新渲染时(jsx 改变),useEffect 都会重新执行

  这一特性在我们进行事件监听时,好像还行;但如果我们进行网络请求、订阅操作时,显然是没有必要的,还会造成一些性能问题。那怎么解决能?

答案

  前面我们仅仅使用了 useEffect() 的第一个参数,其实它还有而二个参数用来标明哪些操作需要重新执行。

需要注意的是,我们标明的是受该操作影响的 state

useEffect(function, array)

  如果我们希望当前的 Effect 不受任何依赖影响,我们可以传入一个空数组

function Demo(props) {
  useEffect(() => {
    console.log('开启订阅')
    console.log('发起网络请求')

    return function cleanup() {
      console.log('取消订阅')
    }
  }, [])

  useEffect(() => {
    console.log('发起网络请求')

    console.log('修改count值')
  }, [count])

  return <div></div>
}
更新于 : 8/7/2024, 2:16:31 PM