热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

揭开ReactHooks的神秘面纱:数组解构融成魔法

我超喜欢React新出的这个HooksAPI。而在使用它时却有一些奇怪的规则。为了那些纠结于为什么要

我超喜欢 React 新出的这个 Hooks API。而在使用它时却有一些奇怪的规则。为了那些纠结于为什么要有这些规则的人,在这里我会以模型图的方式来向你们展示这个新的 API。

警告: Hooks 这项提案仍在测试阶段

这篇文章主要讲述的是关于 React hooks 这项新 API,此时这个提案仍处于 alpha 测试版本。你可以在React 官方文档中找到稳定的 React API。

揭开 React Hooks 的神秘面纱:数组解构融成魔法

图片来自网站 Pexels(rawpixel.com)

拆解 Hooks 的工作方式

“就像魔法一样无法理解”,这是我从一些人口中听到的对于这项新提案的评价,所以我愿意尝试通过拆解这项提案的代码语法去了解它是如何在代码中工作的,至少也要了解它最表层的执行逻辑。

Hooks 的规则

React 的核心团队规定我们在使用 hooks 时必须遵从hooks 提案文档中列出的两条规则。

  • 不要在循环、条件语句或者嵌套函数中调用 Hooks
  • 只能在 React 函数中调用 Hooks

对于后者我觉得是不言而喻的。要将自己的业务代码嵌入到功能组件当中,你自然需要以某种方式将你的代码和组件联系起来。

至于前者我认为也是令人感到困惑的一点,因为这与正常编程时调用 API 的方式相比起来并不寻常,这也是我今天正想探索的一点。

在 hooks 中,状态管理都与数组有关

为了让我们更清晰地理解这个模型,让我们直接实现一个简单的 hooks API 实例看看它可能长什么样子。

请注意这只是推测,并且只是可能的 API 实现方法,主要是为了展示应该通过怎样的思维去理解它。当然这并不一定就是 API 实际的内部工作方式。而且目前这也只是一个提案,在未来一切都可能发生改变

我们可以怎样实现 useState()

让我们通过剖析一个例子来演示一下状态钩子的实现可能会怎么运行吧。

首先让我们从一个组件开始:

function RenderFunctionComponent() {
  const [firstName, setFirstName] = useState("Rudi");
  const [lastName, setLastName] = useState("Yardley");

  return (
    
  );
}

实现 hooks API 的基本思想是把一个 setter 方法作为钩子函数返回的第二个数组项,之后通过这个 setter 方法来控制钩子管理的状态值。(在这个例子中,setter 方法指 setFirstName ,钩子就是 useState 而它管理的值就是"Rudi")

所以让我们来看看 React 将利用它来做些什么?

让我先解释一下在 React 的内部中它可能会怎么工作。下面图表将为我们展示一个特定组件在渲染时其内部执行上下文的执行过程。这也意味着存储在这里的数据是从外面传入的。虽然这里的状态没有与其他组件共享,但是它会继续保留在一定范围之内以供后续渲染对应的特殊组件时使用。

1) 初始化

声明两个空数组: settersstate

设置一个 cursor 参数为 0

揭开 React Hooks 的神秘面纱:数组解构融成魔法

初始化:两个空数组,cursor 参数为 0

2) 初次渲染

第一次执行组件方法

当调用 useState() 时,第一次会将一个 setter 方法(就是以 cursor 为下标的参数)添加到 setters 数组当中然后再把一些状态添加到 state 数组当中。

揭开 React Hooks 的神秘面纱:数组解构融成魔法

初次渲染:cursor ++,变量分别被写入数组当中

3) 后续渲染

后续的每一次渲染过程当中 cursor 都将被重置,而渲染的值都只是从每一次的数组当中取出来的。

揭开 React Hooks 的神秘面纱:数组解构融成魔法

后续渲染:从数组(以 cursor 为下标)中读取了每一项变量的值

4) 事件代理

因为每一个 setter 方法都和其 cursor 绑定以至于通过触发来调用任何 setter 时它都将改变对应索引位置的状态数组的状态值。

揭开 React Hooks 的神秘面纱:数组解构融成魔法

Setters “记住”了他们的索引并根据它来写入内存。

以及底层的实现

这里用一段代码示例来展示:

let state = [];
let setters = [];
let firstRun = true;
let cursor = 0;

function createSetter(cursor) {
  return function setterWithCursor(newVal) {
    state[cursor] = newVal;
  };
}

// 这是我仿写的 useState 辅助类
export function useState(initVal) {
  if (firstRun) {
    state.push(initVal);
    setters.push(createSetter(cursor));
    firstRun = false;
  }

  const setter = setters[cursor];
  const value = state[cursor];

  cursor++;
  return [value, setter];
}

// 我们在组件代码中使用上面的钩子
function RenderFunctionComponent() {
  const [firstName, setFirstName] = useState("Rudi"); // cursor: 0
  const [lastName, setLastName] = useState("Yardley"); // cursor: 1

  return (
    
); } // 这里模拟了 React 的渲染周期 function MyComponent() { cursor = 0; // resetting the cursor return ; // render } console.log(state); // Pre-render: [] MyComponent(); console.log(state); // First-render: ['Rudi', 'Yardley'] MyComponent(); console.log(state); // Subsequent-render: ['Rudi', 'Yardley'] // click the 'Fred' button console.log(state); // After-click: ['Fred', 'Yardley']

为什么这样的规则不可避免?

现在如果我们根据一些外部因素或者甚至是组件状态去把 hooks 命令放在渲染周期里执行会怎样呢?

让我们尝试写一些 React 团队规定限制之外的逻辑:

let firstRender = true;

function RenderFunctionComponent() {
  let initName;
  
  if(firstRender){
    [initName] = useState("Rudi");
    firstRender = false;
  }
  const [firstName, setFirstName] = useState(initName);
  const [lastName, setLastName] = useState("Yardley");

  return (
    
  );
}

这破坏了规则!

在这里我们在条件语句中调用了一个 useState 。一起来看这样会对整个系统造成多大的影响。

被“破坏”的组件第一次渲染

揭开 React Hooks 的神秘面纱:数组解构融成魔法

渲染一个外部的“坏”钩,之后它将会在下次渲染时消失不见。

在此时我们在实例中声明的 firstNamelastName 暂时还带着正确的数据,但接下来让我们看看在第二次渲染时会发生什么:

被“破坏”的组件第二次渲染

揭开 React Hooks 的神秘面纱:数组解构融成魔法

在渲染时移除了钩子之后,我们发现了一个错误。

由于在此时我们的状态存储了错位的数据使 firstNamelastName 被同时赋值为 “Rudi” 。这很明显是不正确的而且它也没有任何作用,但是这也给我们说明了为什么 hooks 具有这样的规则限制。

React 团队正在制定使用规范因为不遵守它们将会使数据错位。

思考一下如何在不违反规则的情况下使用 hooks 操作一组数组

所以现在我们应该非常清楚为什么我们不能在循环或者条件语句中调用 use 钩子。因为事实上我们的代码处理是基于数组解构赋值的,如果你更改了渲染时的调用顺序,那么就会使我们的数据或者事件处理器在解构后没有匹配正确。

所以技巧是考虑让 hooks 用一致的 cursor 来管理数组业务,如果你这么做就可以让它正常的工作。

结论

希望我是建立了一个比较清晰的模型向你们展示了这个新 hooks API 在底层是如何进行工作的。记住这里真正的价值是如何把我们所好奇的点一步步解析出来,所以你只要多注意 hooks API 的规则,这样我们就能更好的利用它。

在 React 组件中 Hooks 是一个高效率的 API。这也是人们对它趋之若鹜的原因,如果你还没想到答案,那么只要把数组存储为状态的形式,就会发现你并没有破坏他们的规则。

我希望将来可以持续再了解一下 useEffects 方法并且尝试对比一下它和 React 的那些生命周期方法有什么区别。

这篇文章尚有不足之处,如果你有更好的建议或者发现了文章中的错误纰漏请及时跟我联系。

你可以在 Twitter 上关注 Rudi Yardley @rudiyardley 或者关注 github _@_ryardley

如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。

掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为掘金 上的英文分享文章。内容覆盖 Android 、 iOS 、 前端 、 后端 、 区块链 、 产品 、 设计 、 人工智能 等领域,想要查看更多优质译文请持续关注 掘金翻译计划 、官方微博、 知乎专栏 。


以上所述就是小编给大家介绍的《揭开 React Hooks 的神秘面纱:数组解构融成魔法》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 我们 的支持!


推荐阅读
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • AI助力游戏开发:六小时内完成Demo、剧本、绘画和配音,网友称之为新概念3“A”大作
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了靠AI六小时开发出游戏Demo,剧本绘画配音一条龙,网友:新概念3“A”大作相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 电话号码的字母组合解题思路和代码示例
    本文介绍了力扣题目《电话号码的字母组合》的解题思路和代码示例。通过使用哈希表和递归求解的方法,可以将给定的电话号码转换为对应的字母组合。详细的解题思路和代码示例可以帮助读者更好地理解和实现该题目。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文主要介绍了gym102222KVertex Covers(高维前缀和,meet in the middle)相关的知识,包括题意、思路和解题代码。题目给定一张n点m边的图,点带点权,定义点覆盖的权值为点权之积,要求所有点覆盖的权值之和膜qn小于等于36。文章详细介绍了解题思路,通过将图分成两个点数接近的点集L和R,并分别枚举子集S和T,判断S和T能否覆盖所有内部的边。文章还提到了使用位运算加速判断覆盖和推导T'的方法。最后给出了解题的代码。 ... [详细]
  • 现在比较流行使用静态网站生成器来搭建网站,博客产品着陆页微信转发页面等。但每次都需要对服务器进行配置,也是一个重复但繁琐的工作。使用DockerWeb,只需5分钟就能搭建一个基于D ... [详细]
  • 原文:http:blog.linjunhalida.comblogpjaxgithub:https:github.comdefunktjquery-pjax ... [详细]
  • windows平台使用NSP拦截具体进程的域名解析过程(xFsRedir的代理功能之域名代理)
    byfanxiushu2022-10-17转载或引用请注明原始作者。xFsRedir软件其中之一的功能就是实现了全方位的网络代理,从主机代理,到本地代理 ... [详细]
  • 个体都会学习的JavaScript之DOM树
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了JavaScript之DOM树相关的知识,希望对你有一定的参考价值。目录 ... [详细]
  • php网站_十周后,62%的PHP网站将运行在一个不受支持的PHP版本上
    本文由编程笔记#小编为大家整理,主要介绍了十周后,62%的PHP网站将运行在一个不受支持的PHP版本上相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 使用Frida框架进行Java函数的Hook:基于Frida框架的native函数Hook实现(1)
    0x01前言关于android的hook以前一直用的xposed来hookjava层的函数,对于so层则利用adbi,但是不知道为什么adbi给我的体验 ... [详细]
  • 20211101CleverTap参与度和分析工具功能平台学习/实践
    1.应用场景主要用于学习CleverTap的使用,该平台主要用于客户保留与参与平台.为客户提供价值.这里接触到的原因,是目前公司用到该平台的服务~2.学习操作 ... [详细]
author-avatar
Belief
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有