前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Hooks】:[译]不是魔法,仅仅是数组

【Hooks】:[译]不是魔法,仅仅是数组

作者头像
WEBJ2EE
发布2021-02-26 16:07:41
6370
发布2021-02-26 16:07:41
举报
文章被收录于专栏:WebJ2EEWebJ2EE
代码语言:javascript
复制
目录
1. 解析 hooks 的工作原理
  1.1. hooks 的 2 个规则
  1.2. hooks 的状态管理用的就是数组
  1.3. 怎么实现 useState()
2. React是怎么做的?
  2.1. 初始化
  2.2. 首次渲染
  2.3. 随后的渲染
  2.4. 事件处理
3. 为什么顺序很重要?
  3.1. 糟糕的首次渲染
  3.2. 糟糕的二次渲染
4. 结论

我是 hooks api 的粉丝,但是,在使用 hooks 的时候,它会有一些奇怪的约束。如果你很难理解这些规则,不妨看看这篇文章。

1. 解析 hooks 的工作原理

先让大家能简单的理解新的 hooks API 的提案。

1.1. hooks 的 2 个规则

react 核心小组在提案文档指出,有 2 个使用规则是开发者必须去遵守的

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

第 2 个规则是很容易理解的,因为 hooks 本来设计的目的就是为了扩展函数式组件。

但是,第1个规则就相对不好理解了,也是这篇文章想去深入探讨的。

1.2. hooks 的状态管理用的就是数组

为了更好的理解,我们来看个简单的hooks的实现

注意:这个只是 hooks 的其中一种可能的实现,而不是 hooks 内部真正的实现

1.3. 怎么实现 useState()

先看个简单的 state hook 的例子

代码语言:javascript
复制
function RenderFunctionComponent() {
  const [firstName, setFirstName] = useState("Rudi");
  const [lastName, setLastName] = useState("Yardley");

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

你可以让 useState 返回一个 setter 函数,作为返回结果数组的第2个元素,这个 setter 函数会控制这个有 hook 生成的 state。

2. React是怎么做的?

我们先标记下 React 内部可能是怎么实现。在渲染一个组件时会执行下图的逻辑。意思是说,数据是被存储在渲染组件之外。其他组件不共享 state,但是 state 可以响应特定组件随后的渲染。

2.1. 初始化

创建2个空的数组:setters 和 state

指针指向:0

2.2. 首次渲染

第一次执行组件函数。

每个 setState 第一次执行,推送一个 setter 函数(绑定一个指针位置)到 setters 数组中,推送一个 state 到 state 数组中。

2.3. 随后的渲染

随后的每次渲染,就是光标的重置,从各个数组中读值

2.4. 事件处理

每个 setter 都有一个指针位置的引用,所以每次调用 setter,都会改变对应的 state 的值。

2.5. 简单实现

下面是一个简单的代码示例实现。

注意:这并不是 hooks 的完整实现,而是给你一个好的思路去思考 hooks 是怎么工作的。

代码语言:javascript
复制
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: 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']

3. 为什么顺序很重要?

如果我们改变 hooks 的顺序,当外部因素或组件 state 变化导致重新渲染时,会发生什么?

让我们试试看

代码语言:javascript
复制
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,这导致了很大的问题。

3.1. 糟糕的首次渲染

这里我们说明了 firstName 和 lastName 2个变量,数据也是正确的。但是让我们看下第 2 次渲染。

3.2. 糟糕的二次渲染

state 存储变得不一致,firstName 和 lastName 都被设置成了 Rudi,这很明显是错误的,但是也让我们明白了为什么 hooks 的规则要这样制定。

现在应该明白了为什么 hooks 不能在条件分支和循环中。因为我们处理的是数据集合的指针,要是你改变了调用顺序,指针会对应不上,从而指向错误的数据或处理器。

4. 结论

关于 hooks api 的运行原理,希望我已经讲的比较明白了。最重要的是把这些重要的点组合起来,注意顺序,使用 hooks api 会得到很大的回报。

hooks 是为 react 组件设计的高效的插件式 api。只要你把 state 当成是数组集的模型,你就不会违反他的规则。

参考:

react-hooks-not-magic-just-arrays: https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-02-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebJ2EE 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档