原文:https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e
React hooks 它不是魔法,是数组
在本文中我将会使用一些图表来给大家解构一下这个新提案
首先要表明的是我是 hooks API 的粉丝。在使用 hooks 的时候会有一些奇怪的约束。现在我将给还在疑惑为什么有这些奇怪的约束的大家展示一个模型 — 应该如何使用这个新的 API 。
警告 Hooks 是一个实验性的提案
这篇文章所介绍的 hooks API 现在还是一个实验性的提案。你能在这个地址找到稳定版本的 React API 。

hooks 该如何使用
很多人都在为新的 hooks API 的草案的『魔法』感到困惑,所以我现在想着尽自己所能尝试着解读一下这个新语法。
hooks 的规则
React 核心团队对 hooks 的使用约束主要有两个,你可以从这个文档上面查看到 hooks proposal。
- 不要在循环语句,条件语句,以及内层嵌套函数中调用 Hooks
- 只能通过 React 的方法发起调用 Hooks
第二条是不言自喻的。为了将一个行为绑定到函数式组件上,你需要将这个行为关联到一个组件上了。
使用这样的一个 API 看上去非常的不自然,这也导致我过去一度困惑不已。所以今天我决定探索一番它。
hooks 中所有的状态管理都是与数组有关的
为了更好的建立一个模型,我们先来看一个 hooks 的简单实现。
请注意这是推测,只是一种可能实现 hooks API 可能的方式以用来帮助您来理解它。 这不一定是 API 在内部的工作方式。 这只是一个提案,未来这个可能发生变化。
怎么实现 ‘useState()’ ?
下面的例子将会展示状态 hook 是如何工作的。
首先我们可以从以下的组件开始:
function RenderFunctionComponent() {
const [firstName, setFirstName] = useState['Rudi'];
const [lastName, setLastName] = useState['Yardley'];
return (
<Button onClick={() => setFirstName('Fred')} >Fred</Button>
)
}
hooks API 背后的意图是你能够使用一个 setter 函数,这个函数是 hook 方法返回数组的第二个对象,并且这个 setter 方法将会通过 hook 管理这个状态(即数组的第一个对象)。
所以 React 准备用这个来做什么呢?
让我们理解一下它在 React 内部如何工作。存储的状态可在执行上下文中用于呈现特定组件。 这意味着存储的数据位于正在呈现的组件之外。 此状态不与其他组件共享,它被维持在一个作用域中,这个作用域属于随后渲染的那个特定组件。
-
初始化
创建两个空数组: ‘setters’ 和 ‘state’
把游标设到 0

-
第一次渲染
第一次执行这个组件函数
每次都会调用 useState() 方法, 当第一次执行的,推一个 setter 函数(绑定游标位置)到 setters 数组中,并且推一些状态到 state数组中。

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

-
事件处理
每个 setter 都有一个对它的光标位置的引用,因此触发对任何 setter 函数的调用,它都将改变状态数组中该位置的状态值。

给大家展示一个简单的实现
以下是代码
let state = [];
let setters = [];
let firstRun = true;
let cursor = 0;
function createSetter(cursor) {
return function setterWithCursor(newVal) {
state[cursor] = newVal;
};
}
// This is the pseudocde for the useState helper
// 下面是 useState 实现的伪代码
export function userState(initVal) {
if (firstRun) {
state.push(initVal);
setters.push(createSetter(cursor));
firstRun = false;
}
const setter = setters[cursor];
const value = state[cursor];
cursor++;
return [value, setter]
}
// 使用这个 hooks 的组件
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>
)
}
// 用来模拟 react 渲染循环
function MyComponent() {
cursor = 0;
return <RenderFunctionComponent />
}
console.log(state); // 预渲染 []
MyComponent();
console.log(state); // 第一次渲染 [Rudi, Yardley]
MyComponent();
console.log(state); // 后续渲染 [Rudi, Yardley]
// 点击 Fred 按钮
console.log(state); // 点击后 [Fred, Yardley]
为什么顺序很重要
如果我们改变了依赖外在因素或者组件状态的渲染循环的 hooks 的顺序后会发生什么呢?
let firstRender = true;
function RenderFucntionComponent() {
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 的一个调用,我们看一下这将会怎样的破坏整个系统。
** 第一次渲染错误的组件

现在这种情况下我们的变量 firstName 和 lastName 都包含正确的数据,下面我们来看看第二次渲染的时候会发生什么:
第二次渲染错误的组件

现在 firstName 和 lastName 都会被设置为 Rudi 因为我们的状态存储出了问题,这是一个很明显的错误,但是这个也就解释了为什么 React
核心团队设置了这么多的规则。
React 团队为 hooks API 的使用设置了这么多的规则,如果不按照它们所说的使用会导致数据错误。
**要考虑好要使用 hooks 操纵数组,如何不违反规则
你现在应该清楚为什么你不能在条件语句或循环语句中使用 hooks 了。 因为我们是在处理指向一组数组的游标,如果更改了 render 中的调用顺序,则游标将无法与数据匹配,那么调用将不会指向正确的数据或处理程序。
正确使用的诀窍就是将 hooks 作为一组需要能保证一致游标的数组来管理,这样做一切都不会出错。
结论
对于那些想要进一步理解这个新的 hooks API 的人,希望我已经构建了一个清晰的模型来帮助大家理解它。请记住它的的真正价值在于能够将关注点组合在一起,只要能够小心的注意顺序问题,那么使用 hooks API 将会感觉到很舒服。
Hooks 是 React 组件里一个高效的 API 。这也是大家为什么对它感到如此兴奋的原因,如果你在考虑使用这样的一个模型,它的状态是以数组的形式的状态存在,那你就可以考虑使用这样的方案,只需要保证按照它的规则使用即可。
同样的我还在关注将来可能会有的 useEffects 方法,在将来我会将它与 React 的组件生命周期方法进行对比。
React hooks 它不是魔法,是数组
首先要表明的是我是 hooks API 的粉丝。在使用 hooks 的时候会有一些奇怪的约束。现在我将给还在疑惑为什么有这些奇怪的约束的大家展示一个模型 — 应该如何使用这个新的 API 。
警告 Hooks 是一个实验性的提案
这篇文章所介绍的 hooks API 现在还是一个实验性的提案。你能在这个地址找到稳定版本的 React API 。
hooks 该如何使用
很多人都在为新的 hooks API 的草案的『魔法』感到困惑,所以我现在想着尽自己所能尝试着解读一下这个新语法。
hooks 的规则
React 核心团队对 hooks 的使用约束主要有两个,你可以从这个文档上面查看到 hooks proposal。
第二条是不言自喻的。为了将一个行为绑定到函数式组件上,你需要将这个行为关联到一个组件上了。
使用这样的一个 API 看上去非常的不自然,这也导致我过去一度困惑不已。所以今天我决定探索一番它。
hooks 中所有的状态管理都是与数组有关的
为了更好的建立一个模型,我们先来看一个 hooks 的简单实现。
怎么实现 ‘useState()’ ?
下面的例子将会展示状态 hook 是如何工作的。
首先我们可以从以下的组件开始:
hooks API 背后的意图是你能够使用一个 setter 函数,这个函数是 hook 方法返回数组的第二个对象,并且这个 setter 方法将会通过 hook 管理这个状态(即数组的第一个对象)。
所以 React 准备用这个来做什么呢?
让我们理解一下它在 React 内部如何工作。存储的状态可在执行上下文中用于呈现特定组件。 这意味着存储的数据位于正在呈现的组件之外。 此状态不与其他组件共享,它被维持在一个作用域中,这个作用域属于随后渲染的那个特定组件。
初始化

创建两个空数组: ‘setters’ 和 ‘state’
把游标设到 0
第一次渲染

第一次执行这个组件函数
每次都会调用
useState()方法, 当第一次执行的,推一个 setter 函数(绑定游标位置)到setters数组中,并且推一些状态到state数组中。后续渲染

每次后续渲染都会重置光标,并且只从每个数组中读取这些值。
事件处理

每个 setter 都有一个对它的光标位置的引用,因此触发对任何 setter 函数的调用,它都将改变状态数组中该位置的状态值。
给大家展示一个简单的实现
以下是代码
为什么顺序很重要
如果我们改变了依赖外在因素或者组件状态的渲染循环的 hooks 的顺序后会发生什么呢?
这是
useState的一个调用,我们看一下这将会怎样的破坏整个系统。** 第一次渲染错误的组件


现在这种情况下我们的变量
firstName和lastName都包含正确的数据,下面我们来看看第二次渲染的时候会发生什么:第二次渲染错误的组件
现在
firstName和lastName都会被设置为Rudi因为我们的状态存储出了问题,这是一个很明显的错误,但是这个也就解释了为什么 React核心团队设置了这么多的规则。
**要考虑好要使用 hooks 操纵数组,如何不违反规则
你现在应该清楚为什么你不能在条件语句或循环语句中使用 hooks 了。 因为我们是在处理指向一组数组的游标,如果更改了 render 中的调用顺序,则游标将无法与数据匹配,那么调用将不会指向正确的数据或处理程序。
正确使用的诀窍就是将 hooks 作为一组需要能保证一致游标的数组来管理,这样做一切都不会出错。
结论
对于那些想要进一步理解这个新的 hooks API 的人,希望我已经构建了一个清晰的模型来帮助大家理解它。请记住它的的真正价值在于能够将关注点组合在一起,只要能够小心的注意顺序问题,那么使用 hooks API 将会感觉到很舒服。
Hooks 是 React 组件里一个高效的 API 。这也是大家为什么对它感到如此兴奋的原因,如果你在考虑使用这样的一个模型,它的状态是以数组的形式的状态存在,那你就可以考虑使用这样的方案,只需要保证按照它的规则使用即可。
同样的我还在关注将来可能会有的
useEffects方法,在将来我会将它与 React 的组件生命周期方法进行对比。