react 之 Hook 简述

React 16.8+ 新增一批,以use前缀开头的函数,让函数组件也可以使用 state 以及其他的 React 特性。 本文简述这批钩子函数(hook)一些使用技巧及应用场景, 并试图探究让 hook 节点结构队列和它的状态可以在外部定位原理

Hook概览

  • useState 函数
    • useState返回一对值:当前状态和一个用于更新它的函数
    • 可以在事件处理函数中或其他一些地方调用这个函数, 类似 class 组件的this.setState
      • 但它不会把新的 state 与旧的 state 进行合并
  • 声明多个 state 变量, 使用数组解构语法,可给 state 变量取不同的名字

State Hook

  • 在函数组件内钩入一些 React State 及生命周期等特性的函数
  • Hook 来复用不同组件之间的状态逻辑,通常只用在函数组件

Effect Hook

  • 在 React 组件中执行过数据获取、订阅或者手动修改过 DOM等操作,谓之"副作用"
  • useEffect 函数 给函数组件增加了操作副作用的能力
    • 默认情况下,React 会在每次渲染后调用副作用函数 —— 包括第一次渲染的时候
    • 调用 useEffect 时,就是在告诉 React 在完成对 DOM 的更改后运行你的“副作用”函数
  • 副作用函数还可以通过返回一个函数来指定如何“清除”副作用
    • 与 useState 一样,可在组件中多次使用 useEffect

Hook 规则

  • Hook 就是 JavaScript 函数,有两个额外规则
    • 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用
    • 只能在 React 的函数组件中调用 Hook(自定义的 Hook 中也可)
  • 官方提供 linter 插件来自动执行这些规则

自定义Hook

  • 在组件之间重用一些状态逻辑
    • 当前两种主流方案:高阶组件和 render props
    • 自定义 Hook 可以让你在不增加组件的情况下达到同样的目的
  • Hook 是一种复用状态逻辑的方式,它不复用 state 本身
    • 自定义Hook类似于一种约定而不是功能
    • 若函数的名字以use开头并调用其他Hook,则称之为自定义Hook
    • useSomething 的命名约定可以让 linter 插件在使用 Hook 的代码中找到 bug
  • 应用场景
    • 表单处理,动画,订阅声明,计时器

其他Hook

  • useContext 不使用组件嵌套就可以订阅 React 的 Context
  • useReducer 通过 reducer 来管理组件本地的复杂 state

Hook系统关键

确保Hook 在React作用域内使用

新特性 Hook

Dispatcher

  • Dispatcher 是一个包含了 hook 函数的共享对象
    • 基于 ReactDOM 的渲染状态,它将会被动态的分配或者清理,并且它将会确保用户不能在 React 组件之外获取到 hook
      • 具体做法 通过一个名为 enableHooks 的标志来启用/禁用 hook,当完成渲染工作后,React 会废弃当前的 dispatcher 并禁止 hook
  • Dispatcher 在每次 hook 的调用中都会被函数 resolveDispatcher() 解析

Hook 队列

在 React 后台,hook 会被表示为节点,并以调用顺序连接起来。 hook 并不是被简单的创建然后丢弃,它们有一套独有的机制,一个 hook 会有数个属性

执行流程

  1. 在初次渲染的时候,它的初始状态会被创建
  2. 它的状态可以在运行时更新
  3. React 可以在后续渲染中记住 hook 的状态
  4. React 能根据调用顺序提供正确的状态
  5. React 知道当前 hook 属于哪个部分
  • React 状态视角
    通常只把组件状态看作一个简单的对象,但当处理 hook 的时候,状态需要被看作是一个队列,每个节点都表示了对象的一个模块,其hook节点简易结构如下

    {
    memoizedState: 'foo',
    next: {
      memoizedState: 'bar',
      next: {
        memoizedState: 'bar',
        next: null
      }
    }
    }

    hook 运行的关键代码在于 memoizedStatenext,其他(如baseState,baseUpdate,queue辅助性)的属性会被useReducer() hook 使用,来缓存发送过的 action 以及基本的状态。在每个函数组件调用前,一个名为 prepareHooks() 的函数将先被调用,在这个函数中,当前结构和 hook 队列中的第一个 hook 节点将被保存在全局变量中。以便达到任何时候调用 hook 函数(useXXX()),都能知道当前运行的上下文

  • Hook 队列的实现逻辑
    一旦更新完成,finishHook() 所在的函数将会被调用,在这个函数中,hook 队列的第一个节点的引用将会被保存在渲染了的结构 memoizedState 属性中,从外部读取某一组件状态变迁

  • State Hook
    useState 这个 hook 在后台使用了 useReducer,并且它将 useReducer 作为预定义的 reducer
    这意味着,useState 返回的结果实际上已经是 reducer 的状态,同时也是 action dispatcher

Hook索引

  • 基础 Hook
    • useState
    • useEffect
    • useContext
  • 额外的 Hook
    • useReducer
    • useCallback
    • useMemo
    • useRef
    • useImperativeHandle
    • useLayoutEffect
    • useDebugValue
本作品采用《CC 协议》,转载必须注明作者和本文链接
pardon110
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
开发者 @ 社科大
文章
134
粉丝
24
喜欢
101
收藏
55
排名:106
访问:8.9 万
私信
所有博文
社区赞助商