前言

自从 React 推出 hooks 的 API 后,相信大家对新 API 都很喜欢,但是它对你如何使用它会有一些奇怪的限制。比如,React 官网介绍了 Hooks 的这样一个限制:

不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。

useState Hook 的工作原理

这个限制并不是 React 团队凭空造出来的,的确是由于 React Hooks 的实现设计而不得已为之。

为了让大家有一个更清晰的思维模型,我将用数组来模拟useState的简单实现。

首先让我们通过一个例子来看看 hook 是如何工作的。

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

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

  

useState hook 背后的思想是,你可以使用 hook 函数返回的数组的第二个数组项作为 setter 函数,并且该 setter 将控制由 hook 管理的状态。

具体实现

1)初始化

创建两个空数组:setters 和 state

将 cursor 设置为 0

2)第一次渲染

首次运行组件函数。

每次useState()调用,在第一次运行时,都会将一个 setter 函数推送到 setters 数组上,然后将一些状态推送到 state 数组上。

3)后续渲染

每次后续渲染都会重置 cursor,并且仅从每个数组中读取这些值。

4)事件处理

每个 setter 都有对其 cursor 的引用,因此通过触发对 setter 的调用,setter 它将更改状态数组中该位置的状态值。

简单代码实现

下面通过一段简单的代码示例来演示该实现。

注意:这并不是 React 的底层实现,但对于我们理解 react hook 的心智模型非常有帮助。

const state = [];
const setters = [];
let cursor = 0; function createSetter(cursor) {
return function setterWithCursor(newVal) {
state[cursor] = newVal;
};
} export function useState(initVal) {
if (state[cursor] === undefined && setters[cursor] === undefined) {
state.push(initVal);
setters.push(createSetter(cursor));
} 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 (
<div>
<button onClick={() => setFirstName('Richard')}>Richard</button>
<button onClick={() => setLastName('Fred')}>Fred</button>
</div>
);
} function MyComponent() {
cursor = 0; // resetting the cursor
return <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 'Richard' button console.log(state); // After-click: ['Richard', 'Yardley']

为什么顺序很重要

现在,如果我们根据某些外部因素甚至组件状态更改渲染周期的钩子顺序会发生什么?

让我们做 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 (
<Button onClick={() => setFirstName("Fred")}>Fred</Button>
);
}  

这里我们有useState的一个条件调用。让我们看看这对系统造成的破坏。

Bad Component 第一次渲染

我们的实例变量firstName和lastName包含正确的数据,但让我们看看第二次渲染会发生什么:

Bad Component 第二次渲染

现在,firstName和lastName发生了错位,我们的状态存储变得不一致了。这就是为什么保持正确顺序的重要性。

总结:

通过对 useState 的简单实现来理解 react hooks 的幕后实现逻辑。考虑将状态作为一组数组存在的模型,那么我们不该违反其对应的使用规则。

理解 React Hooks 心智模型:必须按顺序、不能在条件语句中调用的规则的更多相关文章

  1. 理解 React Hooks

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由志航发表于云+社区专栏 TL;DR 一句话总结 React Hooks 就是在 react 函数组件中,也可以使用类组件(classe ...

  2. 通过 React Hooks 声明式地使用 setInterval

    本文由云+社区发表 作者:Dan Abramov 接触 React Hooks 一定时间的你,也许会碰到一个神奇的问题: setInterval 用起来没你想的简单. Ryan Florence 在他 ...

  3. 关于React Hooks,你不得不知的事

    React Hooks是React 16.8发布以来最吸引人的特性之一.在开始介绍React Hooks之前,让咱们先来理解一下什么是hooks.wikipedia是这样给hook下定义的: In c ...

  4. React Hooks 深入系列

    本文基于近段时间对 hooks 碎片化的理解作一次简单梳理, 个人博客.同时欢迎关注基于 hooks 构建的 UI 组件库 -- snake-design. 在 class 已经融入 React 生态 ...

  5. 实现 React Hooks

    实现 React Hooks UI 开发有两个问题: 展示复用 逻辑复用 展示复用目前基本使用组件化来解决,逻辑复用一直以来都没有特别好的解决方案.React 从一开始的 mixin ,到 高阶组件 ...

  6. react新特性 react hooks

    本文介绍的是react新特性react hooks,本文面向的是有一定react开发经验的小伙伴,如果你对react还不是很熟悉的话我建议你先学习react并多多联系. 首先我们都知道react有3种 ...

  7. 30分钟精通React今年最劲爆的新特性——React Hooks

    你还在为该使用无状态组件(Function)还是有状态组件(Class)而烦恼吗? --拥有了hooks,你再也不需要写Class了,你的所有组件都将是Function. 你还在为搞不清使用哪个生命周 ...

  8. React Hooks用法大全

    前言 在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖 ...

  9. React Hooks 实现和由来以及解决的问题

    与React类组件相比,React函数式组件究竟有何不同? 一般的回答都是: 类组件比函数式组件多了更多的特性,比如 state,那如果有 Hooks 之后呢? 函数组件性能比类组件好,但是在现代浏览 ...

随机推荐

  1. 使用allure工具生成测试报告(基于pytest框架)

    一.allure简介:一个轻量级的,灵活的,支持多语言,多平台的开源report框架 Allure基于标准的xUnit结果输出,但是添加了一些补充数据.任何报告都是通过两个步骤生成的.在测试执行期间( ...

  2. 【C++】map容器的用法

    检测map容器是否为空: 1 #include <iostream> 2 #include<map> 3 #include<string> 4 using name ...

  3. OOP第三次总结Blog

    1. 前言 相比于前一次Blog题目集,此次七八九题目集偏重于类的继承.多态性使用方法以及接口的应用;在设计层面,强调模式复用,不断的迭代,若前期设计不合理,则后续的题目增加新的功能(即可扩展性)将会 ...

  4. Spring MVC 到 Spring BOOT 的简化之路

    背景 Spring vs Spring MVC vs Spring Boot Spring FrameWork Spring 还能解决什么问题 Spring MVC 为什么需要Spring Boot ...

  5. SQL修改表约束实现

    先删除表约束 Alter Table 表名 Drop Constraint 约束名 然后再新建约束(加上级联删除) Alter Table Table_Name Add Constraint FK_T ...

  6. 源代码管理相关命令(Git常用命令、Nuget常用命令、CMD常用命令)

    Git常用命令 源代码工具 工具名称 相关地址 Git Git for Windows VSC Visual Studio Code VSC插件(Gitlen) GitLens - Git super ...

  7. uniapp 微信小程序扫码处理

    1.view  代码 <view class="v-main-scan"> <uni-icons @click="scanCode" clas ...

  8. 为什么要鼓励小型企业使用CRM系统

    如果你是一家小公司的管理者,我相信你必须对工作流程.客户.市场销售.市场营销推广等业务流程进行总体规划和管理方法,这往往会使你的心有馀而力不足,引起 繁忙.心有馀而力不足.交流受到阻碍.管理方法和这样 ...

  9. Python 绘制词云

    文本内容:data(包含很多条文本) 1.分词: import jieba data_cut = data.apply(jieba.lcut) 2.去除停用词: stoplist.txt:链接:htt ...

  10. redis的基本操作指令

    https://www.cnblogs.com/woshimrf/p/5198361.html