[译] React Hooks: 没有魔法,只是数组

[译] React Hooks: 没有魔法,只是数组 原文链接: medium.com/@ryardley/r…

我是 React 新特性 Hooks 的粉丝。但是,在你使用 React Hooks的过程中,有一些看上去 很奇怪的限制 。在本文里,对于那些还在为了理解这些限制而苦苦挣扎的同志,我尝试通过一些列图表的方式,来解释为什么会存在这些限制。

理解hooks怎么运行

我听说很多同学都对hooks像魔法一般的效果感到困惑,因此我将尝试通过浅显的方式,来演示hooks是怎么运行的。

hooks的原则

react团队在怎么使用hooks的 官方文档 中,强调了两点主要的使用原则:

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

  • 只能在 React 函数组件中调用hooks

第二点我认为是显而易见的。为了给 函数组件 增加一些能力(比如 state,类声明周期方法),你当然需要通过一种方式,来把这种能力赋给函数组件,这种方式就是使用hooks。

然而,第一点规则,很容易让人感到困惑。不就是使用一个 API 么,为什么还有这么多限制呢。这也正是我将要在下文里解释的。

hooks中的state管理,只是在操作数组

为了更加清晰的理解hooks,让我们来看看怎么简单实现hooks API。

请注意,下面代码只是一个demo,是为了让我们理解hooks大概是怎么运作的。这不是 React 中的真正内部实现。

怎么实现 useState 呢?

让我们通过一个例子来演示,useState内部大概是怎么运作的。

组件代码如下:

function RenderFunctionComponent() {const [firstName, setFirstName] = useState("Rudi");const [lastName, setLastName] = useState("Yardley");
​return (<Button onClick={() => setFirstName("Fred")}>Fred</Button>);
}
复制代码

useState 实现的功能是,你能通过这个hook返回的 数组 中第二个元素,作为修改这个state的一个setter方法。

那么,React可能会怎么来实现 useState 呢?

让我们来想想react内部会怎么来实现 useState 呢。在下面的实现里,state 是存放在被render的组件外面,并且这个state不会和其他组件共享,同时,在这个组件后续render中,能够通过特定的作用域方式,访问到这个state。

1) state初始化

创建两个空数组,分别用来存放 setters 和 state,将 指针 指到 0 的位置:

2) 组件首次render

当首次render这个函数组件的时候。

每一个 useState 调用,当 首次 执行的时候,在 setter 数组里加入一个 setter 函数(和对应的数组index关联);然后,将 state 加入对应的 state 数组里:

3) 组件后续(非首次)render

后续组件的每次render,指针都会重置为 0 ,每调用一次 useState,都会返回指针对应的两个数组里的 state 和 setter,然后将指针位置 +1。

4)setter调用处理

每一个 setter 函数,都关联了对应的指针位置。当调用某个 setter 函数式,就可以通过这个函数所关联的指针,找到对应的 state,修改state数组里对应位置的值:

最后来看看useState简单的实现

let state = [];
let setters = [];
let firstRun = true;
let cursor = 0;
​
function createSetter(cursor) {return function setterWithCursor(newVal) {state[cursor] = newVal;};
}
​
// This is the pseudocode for the useState helper
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];
}
​
// Our component code that uses hooks
function RenderFunctionComponent() {const [firstName, setFirstName] = useState("Rudi"); // cursor: 0const [lastName, setLastName] = useState("Yardley"); // cursor: 1
​return (<div><Button onClick={() => setFirstName("Richard")}>Richard</Button><Button onClick={() => setFirstName("Fred")}>Fred</Button></div>);
}
​
// This is sort of simulating Reacts rendering cycle
function MyComponent() {cursor = 0; // resetting the cursorreturn <RenderFunctionComponent />; // 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的调用顺序不能变呢?

如果我们根据某些外部变量,或者组件自身的state,改变hooks的调用顺序,会有什么后果呢?

我们来演示下 错误的 做法:

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 (<Button onClick={() => setFirstName("Fred")}>Fred</Button>);
}
复制代码

上面代码里,我们第一个 useState 是在一个 条件分支里。我们来看看这样引入的bug。

1) 第一次render

第一个render之后,我们的两个state,firstName 和 lastName 都对应了正确的值。接下来看看组件第二次render的时候,会发生什么情况。

2) 第二次render

第二次render之后,我们的两个state, firstName和 lastName 都成了 Rudi。这显然是错误的,必须要避免这样使用hooks!但是这也给我们演示了,hooks的调用顺序,为什么不能改变。

react团队明确强调了hooks的2个使用原则,如果不按照这些原则来使用hooks,将会导致我们数据的不一致性!

将hooks的操作想象成数组的操作,你可能不太会违背这些原则

OK,现在你应该清楚,为什么我们不能在条件块或者循环语句里调用hooks了。因为调用hooks的过程中,我们是在操作数组上的指针,如果你在多次render中,改变了hooks的调用顺序,将导致数组上的指针和组件里的 useState 不匹配,从而返回错误的 state 以及 setter 。

结论

希望我基本讲明白了,hooks调用顺序的大概原理。hooks是对react生态的一个很好的优化。人们对hooks感到兴奋,是有原因的。如果你将hooks的操作,当做数组一样来看待,那么你一般不会违背hooks的使用原则。

转载于:https://juejin.im/post/5ce8071ff265da1bd30534c9

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275073.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

管理沟通中移情的应用_移情在设计中的重要性

管理沟通中移情的应用One of the most important aspects of any great design is the empathetic understanding of and connection to the user. If a design is ‘selfish’, as in when a product designed with the designer in mind and not the user, it will ultimatel…

网易前端进阶特训营,邀你免费入营!一举解决面试晋升难题!

网易等大厂的前端岗位一直紧缺&#xff0c;特别是资深级。最近一位小哥面进网易&#xff0c;定级P4&#xff08;资深&#xff09;&#xff0c;总包60W&#xff0c;给大家带来真实面经要点分享。网易的要求有&#xff1a;1.对性能优化有较好理解&#xff0c;熟悉常用调试工具2.熟…

Feign的构建过程及自定义扩展功能

spring-cloud-openfeign-core-2.1.1.RELEASE.jar 中 HystrixFeign 的详细构建过程&#xff1a; EnableFeignClients -> FeignClientsRegistrar 扫描 Feign注解的类 -> FeignClientFactoryBean通过Targeter生产FeignClient -> Targeter通过Feign.Builder构建Feign ->…

angelica类似_亲爱的当归(Angelica)是第一个让我哭泣的VR体验

angelica类似It was a night just like any other night. I finished work for the day and closed my laptop. I had dinner and after an hour, I put on my Oculus Quest headset in order to begin my VR workout.就像其他任何夜晚一样&#xff0c; 这 是一个夜晚。 我完成…

面试官:请手写一个带取消功能的延迟函数,axios 取消功能的原理是什么

大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以点此加我微信 ruochuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。本文仓库 https://githu…

关于base64编码的原理及实现

我们的图片大部分都是可以转换成base64编码的data&#xff1a;image。 这个在将canvas保存为img的时候尤其有用。虽然除ie外&#xff0c;大部分现代浏览器都已经支持原生的基于base64的encode和decode&#xff0c;例如btoa和atob。&#xff08;将canvas画布保存成img并强制改变…

Django web开发系列(五)模板

一 前言在上一节了解到视图函数处理后&#xff0c;会将结果渲染到创建的html页面&#xff0c;但html如何接收并显示视图函数返回的动态数据呢&#xff1f;最常用的做法就是使用模板(Template)&#xff0c;本节将简单介绍一下模板的作用和用法。 可以这样简单的理解模板的概念&a…

facebook 面试_如何为您的Facebook产品设计面试做准备

facebook 面试重点 (Top highlight)Last month, I joined Facebook to work on Instagram DMs and as a way to pay it forward, I 上个月&#xff0c;我加入了Facebook&#xff0c;从事Instagram DM的工作&#xff0c;作为一种支付方式&#xff0c;我 offered to help anyone…

8年了,开始写点东西了

大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以点此加我微信 ruochuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。今天分享一位大佬的文章…

荒径 弗罗斯特_弗罗斯特庞克,颠覆性城市建设者

荒径 弗罗斯特Most gamers are familiar with Will Wright’s famous SimCity series. It created the city building genre and there have been many attempts over the years to ape it. But few developers have been bold enough to completely deconstruct the formula; …

2012年1月份第2周51Aspx源码发布详情

WP7手指画图应用源码 2012-01-14 [VS2010] 游戏介绍&#xff1a;Windows Phone 7手指画图应用 – FingerPaint&#xff0c;您通过此游戏可以随心画一些感兴趣的东西&#xff0c;陶冶情操。操作简单&#xff0c;页面简洁。适合新手学习参考。 WP7 Car Bloke(交通工具开销记录)源…

Gitee 如何自动部署博客 Pages?推荐用这个GitHub Actions!

大家好&#xff0c;我是若川。最近组织了源码共读活动&#xff0c;感兴趣的可以点此加我微信 ruochuan12 参与&#xff0c;每周大家一起学习200行左右的源码&#xff0c;共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。前段时间我把自己的博客…

Java io流学习总结(三)

转载于&#xff1a;https://www.cnblogs.com/ll409546297/p/7197911.html java.io几种读写文件的方式 一、Java把这些不同来源和目标的数据都统一抽象为数据流。 Java语言的输入输出功能是十分强大而灵活的。 在Java类库中&#xff0c;IO部分的内容是很庞大的&#xff0c;因为它…

现在流行的画原型图工具_原型资源图:8种流行原型工具的综合指南

现在流行的画原型图工具Although tools are not the most important things to learn as a UX designer, inevitably you need to use it in order to achieve your more important goals, to solve user’s problems. This article covers today’s 8 popular UX prototyping …

持续5个月,200+笔记,3千多人参与,邀请你来学源码~

注意&#xff1a;本文点击文末阅读原文可查看文中所有链接。我正在参加掘金年度人气作者投票活动&#xff0c;大家有空可以加微信群帮忙投票&#xff0c;感谢大家&#xff01;想起今天还没发文&#xff0c;就开放下微信群二维码&#xff0c;大家扫码进群读源码和帮忙投票吧。群…

自己动手开发调试器 01

背景: 在做XXX编译器检证时经常需要区分是代码端错误&#xff0c;还是编译器端错误&#xff0c;因此对代码进行调试是必不可少的。但是狗日的甲方并没有提供对应的调试器XXXDB&#xff0c;而用GDB调试XXX生成的可执行程序很不稳定&#xff0c;经常出现异常&#xff0c;干脆…

02如何抓住重点,系统高效地学习数据结构与算法?

以下内容总结自极客时间王争大佬的《数据结构与算法之美》课程&#xff0c;本文章仅供个人学习总结。 什么是数据结构?什么是算法? 从广义上讲&#xff0c;数据结构就是指一组数据的存储结构。算法就是操作数据的一组方法。 类比图书馆的书籍&#xff0c;我们如果想找一本书可…

第2年,倒数第3天,1.5万票,感动!

1源码共读大家好&#xff0c;我是若川。众所周知。从8月份开始&#xff0c;我组织了源码共读活动&#xff0c;至今已经有5个月了&#xff0c;每周一期&#xff0c;进行到了第18期。每周坚持写源码解读文章&#xff0c;每天坚持答疑解惑&#xff0c;帮助了很多人学会看源码&…

启发式搜索给神经网络_神经科学如何支持UX启发式

启发式搜索给神经网络重点 (Top highlight)Interaction and UX designers have long known and used heuristics to guide the creation of a user-friendly interface. We know empirically that these principles work, and they make “common sense”. These heuristics th…

Django实战(1):需求分析和设计

Depot是《Agile Web Development with Rails》中的一个购物车应用。 该书中用多次迭代的方法&#xff0c;逐步实现购物车应用&#xff0c;使很多人走上了rails开发的道路。 遗憾的是Django世界中好像没有类似的指引&#xff0c;也许是因为pythoner 不需要具体的例子。 但是如果…