Open EasonYou opened 4 years ago
第一次翻译,会有很多纰漏,主要目的还是自我学习 react-hooks-not-magic-just-arrays React hooks: 没有魔法,只是数组而已
第一次翻译,会有很多纰漏,主要目的还是自我学习 react-hooks-not-magic-just-arrays
当你用react hook的时候会有一些奇怪的约束,这里我提供一些思路,供给如何去理解这些规则。
我听说过有些人对于hooks API的草案的“魔法”非常疑惑且挣扎,所以我想我可以尝试去解释下,至少在表层上,这个功能是如何运作的
react的核心团队约定了一些规则,开发者需要跟着这些规则去使用。这里有两个主要的使用规则
我认为第二条规则是不言而喻的。hooks是函数组件的一个扩展方法,你需要去用各种方式关联到函数组件。
第一条我想可能对有些人会比较疑惑,因为它看起来在并不自然,而这也是我想解释的点。
让我们看一个简单的hooks API可能的实现方式,可以让我们对它的心智模型有清晰的了解。
来看一个例子,来验证state hook是如何工作的
function RenderFunctionComponent() { const [firstName, setFirstName] = useState("Rudi"); const [lastName, setLastName] = useState("Yardley"); return ( <Button onClick={() => setFirstName("Fred")}>Fred</Button> ); }
hooks背后的思想就是,你可以在函数返回数组中的第二个值,拿到一个setter,这个setter会控制你对应的state,这个state则由hook来管理
来解释下这个在react内部会怎么实现。以下的执行过程会在一个执行上下文中运作,来渲染一个特定的组件。这意味着数组存储在被渲染的组件之外的一层。这个state不与其他的组件共享,但是它可以在这个范围内是被渲染在特定的组件的(todo)
初始化 创建两个空数组:setter和state 给这个设置一个为0的指针
首次渲染 函数组件第一次执行
每个useState都被调用,当第一次执行,把setter方法到setters数组,把state,放到state数组
后续渲染 随后的每次渲染,指针都会重置,然后从每个数组中读取这些值
事件绑定 每个setter都可以通过其自身的指针找到对应的state的位置,并改变其state数组中该位置的值
这里的实例代码,对其做了简单的实现
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的循序,那会发生什么呢?
下面这个例子是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,让我们来看看这对整个系统有什么不好的地方
在这里,我们的例子定义了firsName和lastName,这都是正确的数据。但让我们看一下接下来会发生什么
现在firstName和lastName都被设置成Rudi,因为我们的的存储状态变得不一致了。这是一个显而易见的错误且不能执行,但它让我们了解了上面的hooks规则为什么要这样来做约束。
现在对于为什么不能在条件组或者循环中使用hooks应该已经很清晰了。因为我们要去处理数组的指针位置,如果我们在发生渲染的时候改变了它们的顺序,指针将会无法匹配到对应的正确数据或者进行正确的处理程序。
所以管理hooks的诀窍是将它作为一组需要维持其指针状态的数组。
希望我有清楚地描述出hooks的心智模型,以描述其背后到底发生了什么。
当你用react hook的时候会有一些奇怪的约束,这里我提供一些思路,供给如何去理解这些规则。
解释下hooks是如何工作的
我听说过有些人对于hooks API的草案的“魔法”非常疑惑且挣扎,所以我想我可以尝试去解释下,至少在表层上,这个功能是如何运作的
hooks的规则
react的核心团队约定了一些规则,开发者需要跟着这些规则去使用。这里有两个主要的使用规则
我认为第二条规则是不言而喻的。hooks是函数组件的一个扩展方法,你需要去用各种方式关联到函数组件。
第一条我想可能对有些人会比较疑惑,因为它看起来在并不自然,而这也是我想解释的点。
hooks中的管理状态都是跟数组有关
让我们看一个简单的hooks API可能的实现方式,可以让我们对它的心智模型有清晰的了解。
如何实现一个useState()
来看一个例子,来验证state hook是如何工作的
hooks背后的思想就是,你可以在函数返回数组中的第二个值,拿到一个setter,这个setter会控制你对应的state,这个state则由hook来管理
那么react想用这个做什么呢?
来解释下这个在react内部会怎么实现。以下的执行过程会在一个执行上下文中运作,来渲染一个特定的组件。这意味着数组存储在被渲染的组件之外的一层。这个state不与其他的组件共享,但是它可以在这个范围内是被渲染在特定的组件的(todo)
初始化 创建两个空数组:setter和state 给这个设置一个为0的指针
首次渲染 函数组件第一次执行
每个useState都被调用,当第一次执行,把setter方法到setters数组,把state,放到state数组
后续渲染 随后的每次渲染,指针都会重置,然后从每个数组中读取这些值
事件绑定 每个setter都可以通过其自身的指针找到对应的state的位置,并改变其state数组中该位置的值
简单的实现
这里的实例代码,对其做了简单的实现
为什么顺序如此重要?
如果我们根据外部的条件在渲染中改变了hooks的循序,那会发生什么呢?
下面这个例子是react团队认为我们不应该做的
这里我们在条件判断中使用了useState,让我们来看看这对整个系统有什么不好的地方
不良组件的首次渲染
在这里,我们的例子定义了firsName和lastName,这都是正确的数据。但让我们看一下接下来会发生什么
不良组件的二次渲染
现在firstName和lastName都被设置成Rudi,因为我们的的存储状态变得不一致了。这是一个显而易见的错误且不能执行,但它让我们了解了上面的hooks规则为什么要这样来做约束。
只要考虑到hooks是去操作数组的数据的,你就不会破坏这个规则
现在对于为什么不能在条件组或者循环中使用hooks应该已经很清晰了。因为我们要去处理数组的指针位置,如果我们在发生渲染的时候改变了它们的顺序,指针将会无法匹配到对应的正确数据或者进行正确的处理程序。
所以管理hooks的诀窍是将它作为一组需要维持其指针状态的数组。
结语
希望我有清楚地描述出hooks的心智模型,以描述其背后到底发生了什么。