一.概述

1.什么是 Hook

hook(钩子)是一种特殊的消息处理机制,它可以监视系统或者进程中的各种事件消息,截获发往目标窗口的消息并进行处理。

1.1 Hook 例子

  • 函数记录日志、节流、缓存
  • Windows 窗口消息拦截
  • 键盘事件拦截(木马和外挂)
  • 流量识别分析

1.2 React Hook

我们可以通过 Hook 把需要的状态、副作用方法 “钩” 进来,放到函数内部使用。让原来呆板的 React 函数拥有状态和生命周期。

2. Hook 出现的意义

函数一等公民 - 函数式编程
关注状态变化 - 响应式编程
关注分离,逻辑复用

2.1 没有真正使用到 Class 的特性

  • 继承:render 方法无法继承
  • 类方法:实例化和命令式调用场景少

2.2 在组件之间逻辑难复用

高阶组件问题:

  1. 实现和使用比较麻烦。
  2. 组件会有额外节点,多个高阶组件需要层层嵌套。

2.3 生命周期逻辑难维护

  1. 同一类逻辑分散在不同生命周期
  2. 同一个生命周期中包含不同逻辑

2.4 Hook 不擅长的地方

  • 组件实例、异步闭包应用、非幂调用等场景。
  • 在实际开发过程中,需要根据实际的场景来选择 Class 或 Hook。

二.基础 Hook API

1. useState —— 让函数拥有内部状态

const [state, setState] = useState(initialState);
  1. 调用返回一个数组,分别是 state(状态)更新 state 的函数
  2. 在初始渲染期间,返回的 state 与传入的 initialState 相同。
  3. 调用 setState 时可传入新的 state 值,也可以传入更新 state 的回调方法。

2. useEffect —— 让函数可以执行副作用

useEffect(didUpdate, dependencies);
  1. 可以让你在函数组件中执行副作用操作。
  2. 该 Hook 接收一个包含命令式、且可能有副作用代码的函数(didUpdate)。

2.1 函数副作用 和 纯函数 

  • 函数副作用:对主调函数产生额外影响。如修改全局变量(函数外的变量),修改参数或改变外部存储。
  • 纯函数:函数与外界交换数据只有一个唯一渠道:参数返回值

有副作用的函数都不是纯函数。 

2.2 条件执行

  • 传空数组 []:仅函数第一次执行时执行第一个回调方法。
  • 不穿:函数每次执行时都会重新执行第一个回调方法。
  • 传入依赖 [a, b, ...]:函数每次执行时,如果依赖有变化,则重新执行第一个回调方法。
// 仅第一次执行
useEffect(didUpdate, []);

// 每次执行
useEffect(didUpdate);

// a 或 b 有变化时执行
useEffect(didUpdate, [a, b]);

2.3 回调方法返回清理函数

useEffect(function didUpdate(){
    // 做更新工作
    // 返回清除效果
    return function(){
        // 清除工作
    }
}, []);

仅在 依赖变化销毁前执行。 

2.4 useEffect vs useLayoutEffect

  1. 在 DOM 修改后且浏览器渲染之前。
  2. useLayoutEffect 比 useEffect 更早执行。
  3. useEffect 是异步执行,不阻塞渲染;useLayoutEffect 是同步执行,会阻塞渲染。

3. useContext —— 让函数可以接收 context 对象

const value = useContext(MyContext);
  1. React.createContext 创建 Context
  2. Context.Provider 提供 value
  3. useContext 使用 value(类似 Context.Consumer)

3.1 React Context Api 的三大概念

React.createContext()

// React.createContext 这个方法用来创建 context 对象
// 并包含 Provider,Consumer两个组件 <Provider />, <Consumer />
const {Provider, Consumer} = React.createContext();

Provider

// 数据的生产者,通过 value 属性接收存储的公共状态,来传递给子组件或后代组件
<Provider value={/* some value */}>

Consumer

// 数据的消费者,通过订阅 Provider 传入的 context 的值,来实时更新当前组件的状态
<Consumer>
    {value => /* 根据上下文值呈现内容 */}
</Consumer>

4.Hook 规则

  • 规则一:只在 React 函数中调用 Hook
  • 规则二:只在最顶层使用 Hook

记忆单元格:类似数组方式存储 Hooks List,通过调用顺序(Index)获取。 

三.自定义 Hook

1.自定义 Hook 规则

  • 规则一:自定义 Hook 必须以 “use” 开头
  • 规则二:不同组件使用相同 Hook 不共享 state
  • 规则三:同一个组件每次调用 Hook,它都会获取 独立的 state

四.扩展 API

1. useReducer —— 独立状态管理

const [state, dispatch] = useReducer(reducer, initialArg, init);
  1. 是 useState 的替代方案,提供在函数外部管理 state。
  2. 第一个 reducer 参数是 state 的处理函数(确保是纯函数)。
  3. 第二个参数(必选)和第三个参数(可选),负责设置 state 的初始值。
  4. 调用 dispatch 将通过 reducer 来获取并设置新的 state。 

 state:状态;dispatch:触发状态更新;reducer:状态修改器。

2. useCallback —— 缓存函数

const memoizedCallback = useCallback(
    () => {
        doSomething(a, b);
    }, 
    [a, b],
);

执行后返回一个被缓存的(memoized)回调函数,常用于性能优化。 

3. useMemo —— 缓存计算结果

const memoizedValue = useMemo(
    () => computeExpensiveValue(a, b),
    [a, b]
);
  1. 第一个函数参数会在渲染期间执行
  2. 第二个参数依赖列表是执行条件,和 useEffect 一致。 

4. 缓存比较

场景API类型
ClassshouldComponentUpdateClass 组件是否重新 render
React.PureComponentClass 基类,浅比较 props 和 state
HookReact.memoHook 高阶组件,浅比较 props
React.useMemoHook,缓存计算结果
React.useCallbackHook,缓存函数

 React.memo  >  React.useCallback  >  React.useMemo

  1. 优先做函数组件优化。
  2. 缓存传入的方法。
  3. 最后做精细化的渲染缓存控制。

注意:React 的 vDom Diff 已经帮我们避免了大部分 DOM 渲染了。以上这些缓存优化手段是防止重新进行 React render。 

5. useRef —— 函数储存容器

const refContainer = useRef(initialValue);
  1. useRef 调用后返回一个有 current 属性的 “盒子”
  2. 给组件的 ref 传递后,current 被设置为组件实例。
  3. current 可以存储任何对象。

6. useImperativeHandle —— 实例化的“曲线救国”

useImperativeHandle(ref, createHandle, [deps]);
  1. useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。useImperativeHandle 应当与 forwardRef 一起使用。

  2. React.forwardRef:子组件通过React.forwardRef来创建,可以将ref传递到内部的节点或组件,进而实现跨层级的引用。

本文章整理上课 ppt,仅供学习使用。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐