React Hooks从 基础到进阶

React Hooks基础

  • React Hooks 介绍
  • React Hooks 基础

React Hooks 介绍

  1. Hooks 是什么
  2. 为什么要有 Hooks

Hooks 是什么

  • Hooks:钩子、钓钩、钩住
  • HooksReact v16.8 中的新增功能
  • 作用:为函数组件提供状态、生命周期等原本 class 组件中提供的 React 功能
    • 可以理解为通过 Hooks 为函数组件钩入 class 组件的特性
  • 注意:Hooks 只能在函数组件中使用,自此,函数组件成为 React 的新宠儿

React v16.8 版本前后,组件开发模式的对比:

  • React v16.8 以前: class 组件(提供状态) + 函数组件(展示内容)
  • React v16.8 及其以后:
    1. class 组件(提供状态) + 函数组件(展示内容)
    2. Hooks(提供状态) + 函数组件(展示内容)
    3. 混用以上两种方式:部分功能用 class 组件,部分功能用 Hooks+函数组件

注意1:虽然有了 Hooks,但 React 官方并没有计划从 React 库中移除 class。

注意2:有了 Hooks 以后,不能再把函数组件称为无状态组件了,因为 Hooks 为函数组件提供了状态。

为什么要有 Hooks

两个角度:1 组件的状态逻辑复用 2 class 组件自身的问题

  1. 组件的状态逻辑复用:

    • 在 Hooks 之前,组件的状态逻辑复用经历了:mixins(混入)、HOCs(高阶组件)、render-props 等模式。
    • (早已废弃)mixins 的问题:1 数据来源不清晰 2 命名冲突。
    • HOCs、render-props 的问题:重构组件结构,导致组件形成 JSX 嵌套地狱问题。
  2. class 组件自身的问题:

    • 选择:函数组件和 class 组件之间的区别以及使用哪种组件更合适
    • 需要理解 class 中的 this 是如何工作的
    • 相互关联且需要对照修改的代码被拆分到不同生命周期函数中
      • componentDidMount -> window.addEventListener(‘resize’, this.fn)
      • componentWillUnmount -> window.removeEventListener(‘resize’, this.fn)
  • 相比于函数组件来说,不利于代码压缩和优化,也不利于 TS 的类型推导

正是由于 React 原来存在的这些问题,才有了 Hooks 来解决这些问题

hooks的优势

由于原来 React 中存在的问题,促使 React 需要一个更好的自带机制来实现组件状态逻辑复用。

  1. Hooks 只能在函数组件中使用,避免了 class 组件的问题
  2. 复用组件状态逻辑,而无需更改组件层次结构
  3. 根据功能而不是基于生命周期方法强制进行代码分割
  4. 抛开 React 赋予的概念来说,Hooks 就是一些普通的函数
  5. 具有更好的 TS 类型推导
  6. tree- - shaking 友 好,打包时去掉未引用的代码
  7. 更好的压 缩

项目开发中,Hooks 的采用策略:

  • 不推荐直接使用 Hooks 大规模重构现有组件
  • 推荐:新功能用 Hooks,复杂功能实现不了的,也可以继续用 class
  • 找一个功能简单、非核心功能的组件开始使用 hooks

前面学习的 React 知识是有用的

class 组件相关的 API 不用了,比如:

  • class Hello extends Component
  • componentDidMountcomponentDidUpdatecomponentWillUnmount
  • this 相关的用法

原来学习的内容还是要用的,比如:

  • JSX:{}onClick={handleClick}、条件渲染、列表渲染、样式处理等
  • 组件:函数组件、组件通讯
  • 路由
  • React 开发理念:单向数据流状态提升
  • 解决问题的思路、技巧、常见错误的分析等上

useState Hook

概述

问题:Hook 是什么? 一个 Hook 就是一个特殊的函数,让你在函数组件中获取状态等 React 特性
使用模式:函数组件 + Hooks
特点:从名称上看,Hook 都以 use 开头

useState Hook 的基本使用

  • 使用场景:当你想要在函数组件中,使用组件状态时,就要使用 useState Hook 了
  • 作用:为函数组件提供状态(state)
  • 使用步骤:
    1. 导入 useState 函数
    2. 调用 useState 函数,并传入状态的初始值
    3. useState 函数的返回值中,拿到状态和修改状态的函数
    4. 在 JSX 中展示状态
    5. 在按钮的点击事件中调用修改状态的函数,来更新状态
import { useState } from 'react'

const Count = () => {
  // 返回值是一个数组
  const stateArray = useState(0)

  // 状态值 -> 0
  const state = stateArray[0]
  // 修改状态的函数
  const setState = stateArray[1]

  return (
    <div>
      {/* 展示状态值 */}
      <h1>useState Hook -> {state}</h1>
      {/* 点击按钮,让状态值 +1 */}
      <button onClick={() => setState(state + 1)}>+1</button>
    </div>
  )
}
  • 参数:状态初始值。比如,传入 0 表示该状态的初始值为 0
    • 注意:此处的状态可以是任意值(比如,数值、字符串等),而 class 组件中的 state 必须是对象
  • 返回值:数组,包含两个值:1 状态值(state) 2 修改该状态的函数(setState)

使用数组解构简化

比如,要获取数组中的元素:

  1. 原始方式:索引访问
const arr = ['aaa', 'bbb']

const a = arr[0]  // 获取索引为 0 的元素
const b = arr[1]  // 获取索引为 1 的元素
  1. 简化方式:数组解构
    • 相当于创建了两个变量(可以是任意的变量名称)分别获取到对应索引的数组元素
const arr = ['aaa', 'bbb']

const [a, b] = arr
// a => arr[0]
// b => arr[1]

const [state, setState] = arr
  • 使用数组解构简化 useState 的使用
    • 约定:修改状态的函数名称以 set 开头,后面跟上状态的名称
// 解构出来的名称可以是任意名称

const [state, setState] = useState(0)
const [age, setAge] = useState(0)
const [count, setCount] = useState(0)

状态的读取和修改

状态的使用:1 读取状态 2 修改状态

  1. 读取状态:该方式提供的状态,是函数内部的局部变量,可以在函数内的任意位置使用

  2. 修改状态:

  • setCount(newValue) 是一个函数,参数表示:新的状态值
  • 调用该函数后,将使用新的状态值替换旧值
  • 修改状态后,因为状态发生了改变,所以,该组件会重新渲染

组件的更新过程

函数组件使用 useState hook 后的执行过程,以及状态值的变化:

  • 组件第一次渲染:

    1. 从头开始执行该组件中的代码逻辑
    2. 调用 useState(0) 将传入的参数作为状态初始值,即:0
    3. 渲染组件,此时,获取到的状态 count 值为: 0
  • 组件第二次渲染:

    1. 点击按钮,调用 setCount(count + 1) 修改状态,因为状态发生改变,所以,该组件会重新渲染
    2. 组件重新渲染时,会再次执行该组件中的代码逻辑
    3. 再次调用 useState(0),此时 React 内部会拿到最新的状态值而非初始值,比如,该案例中最新的状态值为 1
    4. 再次渲染组件,此时,获取到的状态 count 值为:1

注意:useState 的初始值(参数)只会在组件第一次渲染时生效

也就是说,以后的每次渲染,useState 获取到都是最新的状态值。React 组件会记住每次最新的状态值!

为函数组件添加多个状态

问题:如果一个函数组件需要多个状态,该如何处理?
回答:调用 useState Hook 多次即可,每调用一次 useState Hook 可以提供一个状态。
注意:useState Hook 多次调用返回的 [state, setState] 相互之间,互不影响。

hooks 的使用规则【重要】

注意:React Hooks 只能直接出现在 函数组件 中,不能嵌套在 if/for/其他函数中

否则就会报错:React Hook “useState” is called conditionally. React Hooks must be called in the exact same order in every component render

React 的 useState 这个 Hook 被条件性(放在一个条件判断中)的调用了。

React Hooks 必须要每次组件渲染时,按照相同的顺序来调用所有的 Hooks。

  • 为什么会有这样的规则? 因为 React 是按照 Hooks 的调用顺序来识别每一个 Hook,如果每次调用的顺序不同,导致 React 无法知道是哪一个 Hook
  • 通过开发者工具可以查看到。

useEffect Hook

  1. side effect - 副作用
  2. useEffect 的基本使用
  3. useEffect 的依赖
  4. useEffect 发送请求

side effect - 副作用

使用场景:当你想要在函数组件中,处理副作用(side effect)时,就要使用 useEffect Hook 了
作用:处理函数组件中的副作用(side effect)

问题:副作用(side effect)是什么?
回答:在计算机科学中,如果一个函数或其他操作修改了其局部环境之外的状态变量值,那么它就被称为有副作用
类比,对于 999 感冒灵感冒药来说:

  • )作用:用于感冒引起的头痛,发热,鼻塞,流涕,咽痛等
  • 副作用:可见困倦、嗜睡、口渴、虚弱感

理解:副作用是相对于主作用来说的,一个功能(比如,函数)除了主作用,其他的作用就是副作用
对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM,发送请求,绑定事件)

React 组件的公式:UI = f(state)

常见的副作用(side effect)

  • 数据(Ajax)请求、手动修改 DOM、localStorage 操作等

useEffect 的基本使用

使用场景:当你想要在函数组件中,处理副作用(side effect)时,就要使用 useEffect Hook 了
作用:处理函数组件中的副作用(side effect)
注意:在实际开发中,副作用是不可避免的。因此,react 专门提供了 useEffect Hook 来处理函数组件中的副作用

import { useEffect } from 'react'

useEffect(function effect() {
  document.title = `当前已点击 ${count}`
})

useEffect(() => {
  document.title = `当前已点击 ${count}`
})

解释:

  • 参数:回调函数(称为 effect),就是在该函数中写副作用代码
  • 执行时机:该 effect 会在组件渲染后以及组件更新后执行
  • 相当于componentDidMount + componentDidUpdate

useEffect 的依赖

  • 问题:如果组件中有另外一个状态,另一个状态更新时,刚刚的 effect 回调,也会执行
  • 性能优化:跳过不必要的执行,只在 count 变化时,才执行相应的 effect
useEffect(() => {
  document.title = `当前已点击 ${count}`
}, [count])

解释:

  • 第二个参数:可选的,可省略;也可以传一个数组,数组中的元素可以成为依赖项(deps)
  • 该示例中表示:只有当 count 改变时,才会重新执行该 effect

useEffect 的依赖是一个空数组

useEffect 的第二个参数,还可以是一个空数组([]),表示只在组件第一次渲染后执行 effect
使用场景:1 事件绑定 2 发送请求获取数据 等

useEffect(() => {
  const handleResize = () => {}
  window.addEventListener('resize', handleResize)
}, [])

解释:

  • 该 effect 只会在组件第一次渲染后执行,因此,可以执行像事件绑定等只需要执行一次的操作
    • 此时,相当于 class 组件的 componentDidMount 钩子函数的作用
  • 跟 useState Hook 一样,一个组件中也可以调用 useEffect Hook 多次
  • 推荐:一个 useEffect 只处理一个功能,有多个功能时,使用多次 useEffect

总结 useEffect 的使用

// 触发时机:1 第一次渲染会执行 2 每次组件重新渲染都会再次执行
// componentDidMount + ComponentDidUpdate
useEffect(() => {})

// componentDidMount
// 触发时机:只在组件第一次渲染时执行
useEffect(() => {}, [])

// componentDidMount + componentDidUpdate(判断)
// 触发时机:1 第一次渲染会执行 2 当 count 变化时会再次执行
useEffect(() => {}, [count])

不要对useEffect的依赖项撒谎

const App = () => {
  const [count, setCount] = useState(0)
  useEffect(() => {
    document.title = '点击了' + count + '次'
  }, [])
  return (
    <div>
      <h1>计数器:{count}</h1>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <hr />
    </div>
  )
}

useEffect完全指南:https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/

useEffect 清理副作用

有时候,我们只想**在 React 更新 DOM 之后运行一些额外的代码。**比如发送网络请求,手动变更 DOM,记录日志,这些都是常见的无需清除的操作。

还有一些副作用是需要清除的。例如订阅外部数据源, 开启定时器,注册事件。这种情况下,清除工作是非常重要的,可以防止引起内存泄露!

问题:如何在组件卸载时,解绑事件?此时,就用到 effect 的返回值了

useEffect(() => {
  const handleResize = () => {}
  window.addEventListener('resize', handleResize)
  return () => window.removeEventListener('resize', handleResize)
}, [])

解释:

  • effect 的返回值也是可选的,可省略。也可以返回一个清理函数,用来执行事件解绑等清理操作
  • 清理函数的执行时机:1 组件卸载时 2 effect 重新执行前
    • 此时,相当于 class 组件的 componentWillUnmount 钩子函数的作用
  • 推荐:一个 useEffect 只处理一个功能,有多个功能时,使用多次 useEffect
  • 优势:根据业务逻辑来拆分,相同功能的业务逻辑放在一起,而不是根据生命周期方法名称来拆分代码
  • 编写代码时,关注点集中;而不是上下翻滚来查看代码

将事件处理程序放在 useEffect 内部

// 1 将 resize 事件处理程序放在 effect 回调中,当前这个代码是没有问题的
useEffect(() => {
  const handleResize = () => {
    console.log('window 窗口大小改变了')
  }
  window.addEventListener('resize', handleResize)

  return () => {
    window.removeEventListener('resize', handleResize)
  }
}, [])

// 2 将 resize 事件处理程序拿到 useEffect 的外部,当前这个代码是没有问题的
const handleResize = () => {
  console.log('window 窗口大小改变了')
}

useEffect(() => {
  window.addEventListener('resize', handleResize)

  return () => {
    window.removeEventListener('resize', handleResize)
  }
}, [])

// 3 有依赖项的情况:
useEffect(() => {
  // resize 事件的处理程序
  const handleResize = () => {
    console.log('window 窗口大小改变了', count)
  }

  window.addEventListener('resize', handleResize)

  return () => {
    window.removeEventListener('resize', handleResize)
  }
}, [count])


// 注意:此处的代码,会给一些警告!!! 不要按照这种方式写代码!!!
// 4 如果将 handleResize 放到了 useEffect 外部,React 会给以警告:
//   要么将 handleResize 放到 useEffect 中
//   要么使用 useCallback 这个 hook 来包裹 handleResize
// resize 事件的处理程序
const handleResize = () => {
  console.log('window 窗口大小改变了', count)
}
useEffect(() => {
  console.log('useeffect 执行了')
  window.addEventListener('resize', handleResize)

  return () => {
    window.removeEventListener('resize', handleResize)
  }
}, [handleResize])

// 总结以上几种情况,推荐:在给 window 绑定事件时,将 事件处理程序放在 useEffect 内部。

useEffect 发送请求

在组件中,使用 useEffect Hook 发送请求获取数据(side effect):

useEffect(() => {
  const loadData = async () => {}
  loadData()
}, [])

解释:

  • 注意:effect 只能是一个同步函数,不能使用 async
  • 因为 effect 的返回值应该是一个清理函数,React 会在组件卸载或者 effect 的依赖项变化时重新执行
  • 但如果 effect 是 async 的,此时返回值是 Promise 对象。这样的话,就无法保证清理函数被立即调用
  • 如果延迟调用清理函数,也就没有机会忽略过时的请求结果或取消请求
  • 为了使用 async/await 语法,可以在 effect 内部创建 async 函数,并调用
// 错误演示:

// 不要给 effect 添加 async
useEffect(async () => {}, [])
// https://github.com/facebook/react/issues/14326#issuecomment-441680293

useEffect(() => {
  // 是否取消本次请求
  let didCancel = false

  async function fetchMyAPI() {
    let url = 'http://something/' + productId
    let config = {}
    const response = await myFetch(url)
    // 如果开启其他请求,就忽略本次(过时)的请求结果
    if (!didCancel) {
      console.log(response)
    }
  }

  fetchMyAPI()
  return () => { didCancel = true } // 取消本次请求
}, [productId])

useEffect循环报错问题

useEffect(() => {
  const getList = async () => {
    const res = await axios.get('http://geek.itheima.net/v1_0/channels')
    setList(res.data.data.channels)
  }
  console.log(list)
  getList()
}, [list])

自定义hooks

除了使用内置的 Hooks 之外,还可以创建自己的 Hooks(自定义 Hooks)。

使用场景:将组件状态逻辑提取到可重用的函数(自定义 Hooks)中,实现状态逻辑复用。

内置 Hooks 为函数组件赋予了 class 组件的功能;在此之上,自定义 Hooks 针对不同组件实现不同状态逻辑复用。

  • 自定义 Hooks 是一个函数,约定函数名称必须以 use 开头,React 就是通过函数名称是否以 use 开头来判断是不是 Hooks

  • Hooks 只能在函数组件中或其他自定义 Hooks 中使用,否则,会报错!

  • 自定义 Hooks 用来提取组件的状态逻辑,根据不同功能可以有不同的参数和返回值(就像使用普通函数一样)

Hooks其他API

useRef hook

1.useRef 能够创建一个ref对象,有current属性,{current:null}

const xxRef=useRef(null)

2.通过ref属性关联到某个DOM上,{current:DOM}

3.通过xxRef.current访问到对应的DOM

useContext hook

跨组件通信

import React, { useContext, useState } from 'react'

// 1 创建Context对象
const Context = React.createContext()

export default function App() {
  const [color, setColor] = useState('red')
  return (
    //  根节点  通过value提供数据
    <Context.Provider value={color}>
      <div>
        <h1>我是根组件</h1>
        <div>颜色:{color}</div>
        <button onClick={() => setColor('yellow')}>修改</button>
        <Father></Father>
      </div>
    </Context.Provider>
  )
}

const Father = () => {
  return (
    <div>
      <h3>我是父组件</h3>
      <Child></Child>
    </div>
  )
}

const Child = () => {
  // useContext 获取数据
  const color = useContext(Context)
  return (
    <div>
      <h5>我是子组件---{color}</h5>
    </div>
  )
}

Hooks进阶

概述

根据前面的学习我们知道,Hooks(内置或自定义)只能在函数组件中使用。因此,Hooks 与函数组件是密不可分的。

所以,要想深入理解 Hooks,就必须先理解函数组件的特性,因为这些特性会影响到了 Hooks 的使用。

本节,我们先来理解函数组件的特性。然后,根据这些特性对实际开发产生的影响,我们再来学习 useCallback / useMemo / useRef 等内置 Hooks。

最后,我们来模拟实现 useState / useEffect 这两个 Hooks,来深入理解 Hooks 的实现原理。

函数组件的特性

React 中的函数组件是通过函数来实现的,函数组件的公式:f(state) => UI,即:数据到视图的映射。

函数组件本身很简单,但因为是通过函数实现的,所以,在使用函数组件时,就会体现出函数所具有的特性来。

函数组件的特性说明:

  • 对于函数组件来说,每次状态更新后,组件都会重新渲染。
  • 并且,每次组件更新都像是在给组件拍照。每张照片就代表组件在某个特定时刻的状态。
  • 或者说:组件的每次特定渲染,都有自己的 props/state/事件处理程序 等。
  • 这些照片记录的状态,从代码层面来说,是通过 JS 中函数的闭包机制来实现的。

这就是 React 中函数组件的特性,更加的函数式(利用函数的特性)

import { useState } from 'react'
import ReactDOM from 'react-dom'

// 没有 hooks 的函数组件:
const Counter = ({ count }) => {
  // console.log(count)
  const showCount = () => {
    setTimeout(() => {
      console.log('展示 count 值:', count)
    }, 3000)
  }

  return (
    <div>
      <button onClick={showCount}>点击按钮3秒后显示count</button>
    </div>
  )
}

const App = () => {
  const [count, setCount] = useState(0)

  return (
    <div>
      <h1>计数器:{count}</h1>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <hr />
      {/* 子组件 */}
      <Counter count={count} />
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

函数组件特性带来的问题

函数组件的特性:组件的每次特定渲染,都有自己的 props/state/事件处理程序 等。

该特性,导致了几个问题:

  • 组件每次重新渲染时,组件内部的事件处理程序等函数都会重新创建,导致子组件每次都会接收到不同的 props,从而重复进行不必要的渲染(性能问题)
  • 组件内的事件处理程序等函数中,只能获取到那一次特定渲染时的数据,这是合理的(闭包的原因)。

说明:函数组件配合 Hooks 使用时,会不会因为闭包以及每次都创建新的函数等,让组件变慢?答案:不会!

注意:在没有发现性能问题前,避免过早的性能优化。如果要优化,一定要考虑优化成本是否大于优化后的价值。

对于第一个问题,我们使用 React.memo 配合 useCallback/useMemo 这两个 Hooks 来解决。

对于第二个问题,我们使用 useRef Hook 来解决。

React.memo高阶组件

介绍

React.memo 高阶组件的使用场景说明:

React 组件更新机制:只要父组件状态更新,子组件就会无条件的一起更新。

  • 子组件 props 变化时更新过程:组件代码执行 -> JSX Diff(配合虚拟 DOM)-> 渲染(变化后的内容)【 DOM 操作】。
  • 子组件 props 无变化更新过程:组件代码执行 -> JSX Diff(配合虚拟 DOM)【无 DOM 操作】。

注意:此处更新指的是组件代码执行、JSX 进行 Diff 操作(纯 JS 的操作,速度非常快,不会对性能产生太多影响)。

  • 如果组件 props 改变了,那么,该组件就必须要更新,才能接收到最新的 props。
  • 但是,如果组件 props 没有改变时,组件也要进行一次更新。实际上,这一次更新是没有必要的。

如果要避免组件 props 没有变化而进行的不必要更新(Diff),这种情况下,就要使用 React.memo 高阶组件。

注:对于 class 组件来说,可以使用 PureComponent 或 shouldComponentUpdate 钩子函数来实现

import { useState } from 'react'
import ReactDOM from 'react-dom'

const Child2 = ({ count }) => {
  console.log('Child2 子组件代码执行了')
  return <div style={{ backgroundColor: '#abc' }}>子组件2:{count}</div>
}

const Child1 = () => {
  console.log('Child1 子组件代码执行了')
  return <div style={{ backgroundColor: '#def' }}>子组件1</div>
}

const App = () => {
  const [count, setCount] = useState(0)

  return (
    <div style={{ backgroundColor: 'pink', padding: 10 }}>
      <h1>计数器:{count}</h1>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <hr />

      {/* 子组件 */}
      <Child1 />
      <br />
      <Child2 count={count} />
    </div>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

语法

使用场景:当你想要避免函数组件 props 没有变化而产生的不必要更新时,就要用到 React.memo 了。

作用:记忆组件上一次的渲染结果,在 props 没有变化时复用该结果,避免函数组件不必要的更新

解释:

  • React.memo 是一个高阶组件,用来记忆(memorize)组件。
  • 参数(Child):需要被记忆的组件,或者说是需要避免不必要更新的组件。
  • 返回值(MemoChild):React 记住的 Child 组件。

原理:通过对比检查更新前后 props 是否相同,来决定是否复用上一次的渲染结果,

  • 如果相同,复用上一次的渲染结果;
  • 如果不同,重新渲染组件。

并不是所有的组件都适合使用memo,比如child2组件,每次都需要重新渲染,使用memo反而会使性能变得更低,逻辑也变得更复杂

浅层对比

默认情况下,React.memo 只会对更新前后的 props 进行浅对比(shallow compare)与 PureComponent 相同。

也就是说,对于对象类型的 prop 来说,只会比较引用

  • 如果更新前后的引用相同,复用上一次的渲染结果(不会重新渲染该组件)。
  • 如果更新前后的引用不同,重新渲染该组件。

如果你要手动控制比较过程,可以使用 React.memo 的第二个参数:

解释:

  • 第二个参数:用来比较更新前后 props 的函数。
  • 返回值:如果返回 true,表示记住(不重新渲染)该组件;如果返回 false,表示重新渲染该组件。

useCallback高阶组件

使用场景

在使用 React.memo 时,对于对象类型的 props,只会比较引用(浅对比)。

但是,因为组件每次更新都会创建新的 props 值,比如,新的对象、事件处理程序等(函数组件的特性)。

这就导致:React.memo 在处理对象类型的 props 时,会失效(每次的 props 都是新对象)。

但是,我们还是想让 React.memo 在处理对象类型的 props 时,也有效。

为了让 React.memo 处理对象类型的 props 有效,只要在组件更新期间保持对象类型引用相等,就可以了。

这时候,就要用到以下两个 Hooks:

  • useCallback Hook:记住函数的引用,在组件每次更新时返回相同引用的函数。
  • useMemo Hook:记住任意数据(数值、对象、函数等),在组件每次更新时返回相同引用的数据【功能之一】

基本使用

使用场景:在使用 React.memo 时,为了组件每次更新时都能获取到相同引用的函数,就要用到 useCallback Hook

注意:需要配合 React.memo 高阶函数一起使用

作用:记忆传入的回调函数,这个被记住的回调函数会一直生效,直到依赖项发生改变

解释:

  • 第一个参数:必选,需要被记忆的回调函数。
  • 第二个参数:必选,依赖项数组,用于指定回调函数中依赖(用到)的数据(类似于 useEffect 的第二个参数)。
  • 即使没有依赖,也得传入空数组([]),此时,useCallback 记住的回调函数就会一直生效。
  • 返回值:useCallback 记住的回调函数。
  • useCallback 记住的回调函数会一直生效(或者说会一直返回同一个回调函数),直到依赖项发生改变。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值