(译)React hooks:它不是一种魔法,只是一个数组——使用图表揭秘提案规则

原文地址:https://medium.com/@ryardley/...javascript

译文:染陌 (Githubhtml

译文地址:https://github.com/answershuto/Blogjava

转载请著名出处react

我是一名hooks API的忠实粉丝,然而它对你的使用会有一些奇怪的约束,因此我在本文中使用一个模型来把原理展现给那些想去使用新的API却难以理解它的规则的人。git

警告:Hooks 还处于实验阶段

本文提到的 Hooks API 还处于实验阶段,若是你须要的是稳定的 React API 文档,能够从这里找到。github

解密 Hooks 的工做方式

我发现一些同窗苦苦思索新的 Hooks API 中的“魔法”,因此我打算尝试着去解释一下,至少从表层出发,它是如何工做的。编程

Hooks 的规则

React 核心团队在Hooks的提案中提出了两个在你使用Hooks的过程当中必须去遵照的主要规则。数组

  • 请不要在循环、条件或者嵌套函数中调用 Hooks
  • 都有在 React 函数中才去调用 Hooks

后者我以为是显而易见的,你须要用函数的方式把行为与组件关联起来才能把行为添加到组件。函数

然而对于前者,我认为它会让人产生困惑,由于这样使用 API 编程彷佛显得不那么天然,但这就是我今天要套索的内容。this

Hooks 的状态管理都是依赖数组的

为了让你们产生一个更清晰的模型,让咱们来看一下 Hooks 的简单实现多是什么样子。

须要注意的是,这部份内容只是 API 的一种可能实现方法,以便读者更好地趣理解它。它并非 API 实际在内部的工做方式,并且它只是一个提案,在将来都会有可能发生变化。

咱们应该如何实现“useState()”呢?

让咱们经过一个例子来理解状态多是如何工做的。

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

代码地址

/* 译:https://github.com/answershuto */
function RenderFunctionComponent() {
  const [firstName, setFirstName] = useState("Rudi");
  const [lastName, setLastName] = useState("Yardley");

  return (
    <Button onClick={() => setFirstName("Fred")}>Fred</Button>
  );
}

Hooks API 背后的思想是你能够将一个 setter 函数经过 Hook 函数的第二个参数返回,用该函数来控制 Hook 管理的壮体。

因此 React 能用这个作什么呢?

首先让咱们解释一下它在 React 内部是如何工做的。在执行上下文去渲染一个特殊组件的时候,下面这些步骤会被执行。这意味着,数据的存储是独立于组件以外的。该状态不能与其余组件共享,可是它拥有一个独立的做用域,在该做用域须要被渲染时读取数据。

(1)初始化

建立两个空数组“setters”与“state”

设置指针“cursor”为 0

(2)首次渲染

首次执行组件函数

每当 useState() 被调用时,若是它是首次渲染,它会经过 push 将一个 setter 方法(绑定了指针“cursor”位置)放进 setters 数组中,同时,也会将另外一个对应的状态放进 state 数组中去。

(3)后续渲染

每次的后续渲染都会重置指针“cursor”的位置,并会从每一个数组中读取对应的值。

(4)处理事件

每一个 setter 都会有一个对应的指针位置的引用,所以当触发任何 setter 调用的时候都会触发去改变状态数组中的对应的值。

以及底层的实现

这是一段示例代码:

代码地址

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

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

/* 译:https://github.com/answershuto */
// 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];
}

/* 译:https://github.com/answershuto */
// Our component code that uses 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={() => setFirstName("Fred")}>Fred</Button>
    </div>
  );
}

// This is sort of simulating Reacts rendering cycle
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 '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 (
    <Button onClick={() => setFirstName("Fred")}>Fred</Button>
  );
}

咱们在条件语句中调用了 useState 函数,让咱们看看它对整个系统形成的破坏。

糟糕组件的首次渲染

到此为止,咱们的变量 firstName 与 lastName 依旧包含了正确的数据,让咱们继续去看一下第二次渲染会发生什么事情。

糟糕的第二次渲染

如今 firstName 与 lastName 这两个变量所有被设置为“Rudi”,与咱们实际的存储状态不符。

这个例子的用法显然是不正确的,可是它让咱们知道了为何咱们必须使用 React 团队规定的规则去使用 Hooks。

React 团队制定了这个规则,是由于若是不遵循这套规则去使用 Hooks API会致使数据有问题。

思考 Hooks 维护了一些列的数组,因此你不该该去违反这些规则

因此你如今应该清除为何你不该该在条件语句或者循环语句中使用 Hooks 了。由于咱们维护了一个指针“cursor”指向一个数组,若是你改变了 render 函数内部的调用顺序,那么这个指针“cursor”将不会匹配到正确的数据,你的调用也将不会指向正确的数据或句柄。

所以,有一个诀窍就是你须要思考 Hooks 做为一组须要一个匹配一致的指针“cursor”去管理的数组(染陌译)。若是作到了这一点,那么采用任何的写法它均可以正常工做。

总结

但愿经过上述的讲解,我已经给你们创建了一个关于 Hooks 的更加清晰的思惟模型,以此能够去思考新的 Hooks API 底层到底作了什么事情。请记住,它真正的价值在于可以关注点汇集在一块儿,同时当心它的顺序,那使用 Hooks API 会很高的回报。

Hooks 是 React 组件的一个颇有用的插件,这也佐证了为什么你们为什么对此感到如此兴奋。若是你脑海中造成了我上述的这种思惟模型,把这种状态做为一组数组的存在,那么你就会发如今使用中不会打破它的规则。

我但愿未来再去研究一下 useEffects useEffects 方法,并尝试将其与 React 的生命周期进行比较。

这篇文章是一篇在线文档,若是你想要参与贡献或者有任何有误的地方,欢迎联系我。

你能够在 Twitter 上面 fllow 我(Rudi Yardley)或者在Github找到我。

染陌 译:https://github.com/answershuto

相关文章
相关标签/搜索