原文地址:https://medium.com/@ryardley/...javascript
译文:染陌 (Github)html
译文地址:https://github.com/answershuto/Blogjava
转载请著名出处react
我是一名hooks API的忠实粉丝,然而它对你的使用会有一些奇怪的约束,因此我在本文中使用一个模型来把原理展现给那些想去使用新的API却难以理解它的规则的人。git
本文提到的 Hooks API 还处于实验阶段,若是你须要的是稳定的 React API 文档,能够从这里找到。github
我发现一些同窗苦苦思索新的 Hooks API 中的“魔法”,因此我打算尝试着去解释一下,至少从表层出发,它是如何工做的。编程
React 核心团队在Hooks的提案中提出了两个在你使用Hooks的过程当中必须去遵照的主要规则。数组
后者我以为是显而易见的,你须要用函数的方式把行为与组件关联起来才能把行为添加到组件。函数
然而对于前者,我认为它会让人产生困惑,由于这样使用 API 编程彷佛显得不那么天然,但这就是我今天要套索的内容。this
为了让你们产生一个更清晰的模型,让咱们来看一下 Hooks 的简单实现多是什么样子。
须要注意的是,这部份内容只是 API 的一种可能实现方法,以便读者更好地趣理解它。它并非 API 实际在内部的工做方式,并且它只是一个提案,在将来都会有可能发生变化。
让咱们经过一个例子来理解状态多是如何工做的。
首先让咱们从一个组件开始:
/* 译: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 内部是如何工做的。在执行上下文去渲染一个特殊组件的时候,下面这些步骤会被执行。这意味着,数据的存储是独立于组件以外的。该状态不能与其余组件共享,可是它拥有一个独立的做用域,在该做用域须要被渲染时读取数据。
建立两个空数组“setters”与“state”
设置指针“cursor”为 0
首次执行组件函数
每当 useState() 被调用时,若是它是首次渲染,它会经过 push 将一个 setter 方法(绑定了指针“cursor”位置)放进 setters 数组中,同时,也会将另外一个对应的状态放进 state 数组中去。
每次的后续渲染都会重置指针“cursor”的位置,并会从每一个数组中读取对应的值。
每一个 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 了。由于咱们维护了一个指针“cursor”指向一个数组,若是你改变了 render 函数内部的调用顺序,那么这个指针“cursor”将不会匹配到正确的数据,你的调用也将不会指向正确的数据或句柄。
所以,有一个诀窍就是你须要思考 Hooks 做为一组须要一个匹配一致的指针“cursor”去管理的数组(染陌译)。若是作到了这一点,那么采用任何的写法它均可以正常工做。
但愿经过上述的讲解,我已经给你们创建了一个关于 Hooks 的更加清晰的思惟模型,以此能够去思考新的 Hooks API 底层到底作了什么事情。请记住,它真正的价值在于可以关注点汇集在一块儿,同时当心它的顺序,那使用 Hooks API 会很高的回报。
Hooks 是 React 组件的一个颇有用的插件,这也佐证了为什么你们为什么对此感到如此兴奋。若是你脑海中造成了我上述的这种思惟模型,把这种状态做为一组数组的存在,那么你就会发如今使用中不会打破它的规则。
我但愿未来再去研究一下 useEffects useEffects 方法,并尝试将其与 React 的生命周期进行比较。
这篇文章是一篇在线文档,若是你想要参与贡献或者有任何有误的地方,欢迎联系我。
你能够在 Twitter 上面 fllow 我(Rudi Yardley)或者在Github找到我。