Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(翻译)React hooks:不是魔法,仅仅是数组 #12

Open
tianma630 opened this issue Aug 27, 2020 · 0 comments
Open

(翻译)React hooks:不是魔法,仅仅是数组 #12

tianma630 opened this issue Aug 27, 2020 · 0 comments
Labels

Comments

@tianma630
Copy link
Owner

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

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

解析 hooks 的工作原理

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

hooks的2个规则

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

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

第2个规则是很容易理解的,因为 hooks 本来设计的目的就是为了扩展函数式组件。
但是,第1个规则就相对不好理解了,也是这篇文章想去深入探讨的。

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

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

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

怎么实现 useState()

先看个简单的 state hook 的例子

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。

React是怎么做的

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

1) 初始化

创建2个空的数组:setters 和 state
光标指向0
image.png
初始化:2个空的数组,光标是0

2) 首次渲染

第一次只想组件函数
每个 setState 第一次执行,推送一个 setter 函数(绑定一个光标位置)到 setters 数组中,推送一个 state 到 state 数组中
image.png
首次渲染:随着光标增加,各项被写入到数组中

3) 随后的渲染

随后的每次渲染,就是光标的重置,从各个数组中读值
image.png
随后的渲染:随着光标增加,各项从数组中被读取

4) 事件处理

每个 setter 都有一个光标位置的引用,所以每次调用 setter,都会改变对应的 state 的值。
image.png
setters 会记住他们的位置,根据位置去修改存储

简单实现

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

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

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']

为什么顺序很重要

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

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,这导致了很大的问题。

糟糕的首次渲染

image.png
渲染了一个错误的 hook,这个 hook 在后续的渲染会消失

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

糟糕的第二次渲染
image.png
在渲染的时候去掉了一个 hook,导致了错误

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

不遵守 react team 制定的规则,会导致数据不一致

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

结论

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant