React Hook 原理,及如何使用Hook

news/2024/7/15 18:15:31 标签: react.js, 前端, 前端框架

一、 Hook使用规则 

  • 只在最顶层使用Hook

  • 不要在循环,条件或嵌套函数中调用Hook;

  • 只在组件函数和自定义hook中调用Hook

Q1 : 为什么 hook 不能 在循环,条件或嵌套函数中调用Hook ? 

 A1:  因为这跟React的渲染函数和React Hook的实现原理有关,如果在循环,条件或嵌套函数中调用hook,会影响到了React自身记录的Hook顺序,会导致组件状态(值)不一致问题。

Q2: 为什么组件内,刷新了一次后,useState 仍然能保持最新的值,而不是回到初始值? 

A2:  这是因为,useState 这个hook底层逻辑是利用闭包原理,它会把最新的值,初始化的值,下一个要调用的hook这些值创建成一个fiber对象,后面每次刷新都会从这个fiber对象上获取。

     

二、 简单实现一个 useState 

demo1 : 问题在每次刷新,值都是初始化

function useState(initialValue) {
  var state = initialValue;
  function setState(newState) {
    state = newState;
    render();
  }
  return [state, setState];
}

demo2 :  问题在,如果组件内使用多个useState, _state 值会被覆盖

var _state; // 把 state 存储在外面

function useState(initialValue) {
  _state = _state || initialValue; // 如果没有 _state,说明是第一次执行,把 initialValue 复制给它
  function setState(newState) {
    _state = newState;
    render();
  }
  return [_state, setState];
}

demo3:    按照hook的顺序,把state值依次存进  memoizedState  中 。

这就是为什么  hook 不能 在循环,条件或嵌套函数中调用Hook , 因为这样会影响React自身记录的Hook顺序。

let memoizedState = []; // hooks 存放在这个数组
let cursor = 0; // 当前 memoizedState 下标

function useState(initialValue) {
  memoizedState[cursor] = memoizedState[cursor] || initialValue;
  const currentCursor = cursor;
  function setState(newState) {
    memoizedState[currentCursor] = newState;
    render();
  }
  return [memoizedState[cursor++], setState]; // 返回当前 state,并把 cursor 加 1
}

三、 真正实现一个 useState 

虽然我们用数组基本实现了一个可用的 Hooks,了解了 Hooks 的原理,但在 React 中,实现方式却有一些差异的。

  • React 中是通过类似单链表的形式来代替数组的。通过 next 按顺序串联所有的 hook。

    type Hooks = {
    	memoizedState: any, // 指向当前渲染节点 Fiber
      baseState: any, // 初始化 initialState, 已经每次 dispatch 之后 newState
      baseUpdate: Update<any> | null,// 当前需要更新的 Update ,每次更新完之后,会赋值上一个 update,方便 react 在渲染错误的边缘,数据回溯
      queue: UpdateQueue<any> | null,// UpdateQueue 通过
      next: Hook | null, // link 到下一个 hooks,通过 next 串联每一 hooks
    }
     
    type Effect = {
      tag: HookEffectTag, // effectTag 标记当前 hook 作用在 life-cycles 的哪一个阶段
      create: () => mixed, // 初始化 callback
      destroy: (() => mixed) | null, // 卸载 callback
      deps: Array<mixed> | null,
      next: Effect, // 同上 
    };

  • memoizedState,cursor 是存在哪里的?如何和每个函数组件一一对应的?

    我们知道,react 会生成一棵组件树(或Fiber 单链表),树中每个节点对应了一个组件,hooks 的数据就作为组件的一个信息,存储在这些节点上,伴随组件一起出生,一起死亡。

参考: 

【React全解3】React.useState原理详解,一次性搞懂useState_react usestate-CSDN博客

React Hooks 原理 · Issue #26 · brickspert/blog · GitHub


http://www.niftyadmin.cn/n/5310439.html

相关文章

面试经典150题(67-71)

leetcode 150道题 计划花两个月时候刷完&#xff0c;今天&#xff08;第三十四天&#xff09;完成了5道(67-71)150&#xff1a; 67.&#xff08;114. 二叉树展开为链表&#xff09;题目描述&#xff1a; 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#…

和可被K整除的子数组(Java详解)

目录 一、题目描述 二、题解 思路分析 具体实现 完整代码 一、题目描述 给定一个整数数组 nums 和一个整数 k &#xff0c;返回其中元素之和可被 k 整除的&#xff08;连续、非空&#xff09; 子数组 的数目。 子数组 是数组的 连续 部分。 示例&#xff1a; 输入&#…

剑指offer面试题3 二维数组中的查找

考察点&#xff1a; 考察数据结构二维数组知识点&#xff1a; 1.java中的数据类型分为基本类型和引用类型&#xff0c;数组属于引用类型&#xff0c;引用类型的变量中存储的是地址&#xff0c;该地址指向内存中的某个对象&#xff0c;参考c中的指针。2.一维数组定义&#xff0c…

android常用方法

获取应用安装来源 private String getAppInstaller(Context context, String packageName) {return context.getPackageManager().getInstallerPackageName(packageName);}判断是否系统应用 在/system/app 或者 /system/priv-app目录下的应用。 public boolean isSystem(Conte…

FreeRTOS学习第6篇–任务状态挂起恢复删除等操作

目录 FreeRTOS学习第6篇--任务状态挂起恢复删除等操作任务的状态设计实验IRReceiver_Task任务相关代码片段实验现象本文中使用的测试工程 FreeRTOS学习第6篇–任务状态挂起恢复删除等操作 本文目标&#xff1a;学习与使用FreeRTOS中的几项操作&#xff0c;有挂起恢复删除等操作…

jmeter关联依赖---三种

1.正则表达式提取器 2.xpath取样器 3.json提取器

e2studio开发LPS28DFW气压计(1)----轮询获取气压计数据

e2studio开发LPS28DFW气压计.1--轮询获取气压计数据 概述视频教学样品申请完整代码下载产品特性通信模式速率新建工程工程模板保存工程路径芯片配置工程模板选择时钟设置UART配置UART属性配置设置e2studio堆栈e2studio的重定向printf设置R_SCI_UART_Open()函数原型回调函数user…

Dockerfile的ENV

文章目录 环境总结测试测试1测试2测试3测试4测试5测试6 参考 环境 RHEL 9.3Docker Community 24.0.7 总结 如果懒得看测试的详细信息&#xff0c;可以直接看结果&#xff1a; 一条 ENV 指令可以定义多个环境变量。Dockerfile里可以包含多条 ENV 指令。环境变量的值不需要用…