资讯专栏INFORMATION COLUMN

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

fjcgreat / 2765人阅读

摘要:它并不是实际在内部的工作方式,而且它只是一个提案,在未来都会有可能发生变化。这意味着,数据的存储是独立于组件之外的。因此,有一个诀窍就是你需要思考作为一组需要一个匹配一致的指针去管理的数组染陌译。

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

译文:染陌 (Github)

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

转载请著名出处

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

警告:Hooks 还处于实验阶段

本文提到的 Hooks API 还处于实验阶段,如果你需要的是稳定的 React API 文档,可以从这里找到。

解密 Hooks 的工作方式

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

Hooks 的规则

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

请不要在循环、条件或者嵌套函数中调用 Hooks

都有在 React 函数中才去调用 Hooks

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

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

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

为了让大家产生一个更清晰的模型,让我们来看一下 Hooks 的简单实现可能是什么样子。

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

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

让我们通过一个例子来理解状态可能是如何工作的。

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

代码地址

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

  return (
    
  );
}

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 (
    
); } // This is sort of simulating Reacts rendering cycle function MyComponent() { cursor = 0; // resetting the cursor return ; // 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 (
    
  );
}

我们在条件语句中调用了 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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99369.html

相关文章

  • 」setState如何知道该做什么?

    摘要:本文翻译自原作者如果有任何版权问题,请联系当你在组件中调用时,你觉得会发生什么当然,会用这条状态重新渲染组件并且更新匹配到的,然后返回元素。如果你之前使用过一些渲染器比如说,你可能知道在页面中使用超过一个渲染器是没什么问题的。 本文翻译自:How Does setState Know What to Do?原作者:Dan Abramov 如果有任何版权问题,请联系shuirong199...

    OldPanda 评论0 收藏0
  • 】函数组件和类组件有什么不同?

    摘要:但是,你可能已经注意到,当你试图通过指定依赖数组来优化时,可能会遇到带有过时闭包的错误。这是否意味着闭包是问题所在我不这么认为。到目前为止,我所看到的所有情况下,过时的闭包问题都是由于错误地假设函数不更改或总是相同而发生的。 原文链接:https://overreacted.io/how-ar... 在很长一段时间内,标准答案是class components提供更多的特性(像sta...

    gself 评论0 收藏0
  • 「每日一瞥

    摘要:即使中没有错误,仍然会执行,这一点一般都是知道的。我们认为这是正确的前进道路,兼具战略性和务实性降低使用门槛开发人员迁移到的障碍之一是从到的并不轻松的迁移。下一步将通过一系列功能和插件为的平滑过渡提供支持,并以此回馈社区。 showImg(https://segmentfault.com/img/remote/1460000017516912?w=1200&h=630); useSt...

    XboxYan 评论0 收藏0
  • 正在失业中的《课多周刊》(第3期)

    摘要:正在失业中的课多周刊第期我们的微信公众号,更多精彩内容皆在微信公众号,欢迎关注。若有帮助,请把课多周刊推荐给你的朋友,你的支持是我们最大的动力。是一种祸害译本文浅谈了在中关于的不好之处。浅谈超时一运维的排查方式。 正在失业中的《课多周刊》(第3期) 我们的微信公众号:fed-talk,更多精彩内容皆在微信公众号,欢迎关注。 若有帮助,请把 课多周刊 推荐给你的朋友,你的支持是我们最大的...

    robin 评论0 收藏0
  • 正在失业中的《课多周刊》(第3期)

    摘要:正在失业中的课多周刊第期我们的微信公众号,更多精彩内容皆在微信公众号,欢迎关注。若有帮助,请把课多周刊推荐给你的朋友,你的支持是我们最大的动力。是一种祸害译本文浅谈了在中关于的不好之处。浅谈超时一运维的排查方式。 正在失业中的《课多周刊》(第3期) 我们的微信公众号:fed-talk,更多精彩内容皆在微信公众号,欢迎关注。 若有帮助,请把 课多周刊 推荐给你的朋友,你的支持是我们最大的...

    Joyven 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<