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

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

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

译文:染陌 (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 (
    <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

目录
相关文章
|
5天前
|
设计模式 存储 前端开发
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
|
1月前
|
前端开发 JavaScript 安全
TypeScript在React Hooks中的应用:提升React开发的类型安全与可维护性
【7月更文挑战第17天】TypeScript在React Hooks中的应用极大地提升了React应用的类型安全性和可维护性。通过为状态、依赖项和自定义Hooks指定明确的类型,开发者可以编写更加健壮、易于理解和维护的代码。随着React和TypeScript的不断发展,结合两者的优势将成为构建现代Web应用的标准做法。
|
14天前
|
前端开发 开发者
彻底颠覆!React Hooks带来前端开发的革命,你准备好了吗?
【8月更文挑战第6天】在现代Web开发中,React作为顶级前端框架,以高效性能和丰富生态著称。React Hooks自16.8版本引入,赋予函数组件使用状态和生命周期的能力,使代码更简洁、模块化,易于维护。常用Hooks如`useState`、`useEffect`等简化了状态管理和副作用操作。Hooks不仅增强了组件能力,提高了代码可读性和可维护性,还在各种应用场景中展现出强大功能,尤其是在中大型项目中优化了代码结构和提升了开发效率。总之,React Hooks为前端开发注入了新活力,推动了更高效便捷的开发实践。
26 1
|
1月前
|
前端开发
React Hooks实战技巧:提升你的组件开发效率
【7月更文挑战第16天】React Hooks为React开发带来了革命性的变化,使得函数式组件更加强大和灵活。通过掌握上述实战技巧,你可以更高效地编写清晰、可维护和可复用的React组件。希望这些技巧能帮助你在React开发之路上走得更远。
|
10天前
|
JavaScript 前端开发 API
浅谈:为啥 Vue 和 React 都选择了 Hooks?
浅谈:为啥 Vue 和 React 都选择了 Hooks?
|
1月前
|
前端开发 JavaScript 开发者
前端框架与库 - React生命周期与Hooks
【7月更文挑战第13天】React 框架革新UI构建,引入Hooks简化组件状态管理和副作用处理。组件生命周期涉及挂载、更新、卸载,对应不同方法,如`componentDidMount`、`shouldComponentUpdate`等,但现在推荐使用`useState`和`useEffect` Hooks。`useEffect`处理副作用,需注意清理和依赖数组。避免问题的关键在于正确使用Hooks和理解其工作模式,以构建高效应用。
|
1月前
|
前端开发 JavaScript 数据格式
react18【系列实用教程】Hooks (useState,useReducer,useRef,useEffect,useContext,useMemo,useCallback,自定义 Hook )
react18【系列实用教程】Hooks (useState,useReducer,useRef,useEffect,useContext,useMemo,useCallback,自定义 Hook )
40 1
|
1月前
|
缓存 前端开发 JavaScript
React Hooks(实例及详解)
【7月更文挑战第2天】React Hooks(实例及详解)
33 3
|
1月前
|
存储 前端开发 JavaScript
react hooks 学习进阶
【7月更文挑战第12天】 React Hooks(自16.8版起)让函数组件能处理状态和副作用。useState用于添加状态管理,useEffect处理副作用,useContext共享数据,useReducer处理复杂状态逻辑,useRef获取引用。进阶技巧涉及性能优化,如useMemo和useCallback,以及遵循规则避免在不适当位置调用Hooks。理解异步更新机制和结合Redux等库提升应用复杂性管理。持续学习新技巧是关键。
30 0
|
1月前
|
前端开发
react18【系列实用教程】Hooks 闭包陷阱 (2024最新版)含useState 闭包陷阱,useEffect 闭包陷阱,useCallback 闭包陷阱
react18【系列实用教程】Hooks 闭包陷阱 (2024最新版)含useState 闭包陷阱,useEffect 闭包陷阱,useCallback 闭包陷阱
28 0