做者:Rudi Yardleyhtml
图解hooks的规则react
我是新的hooks API的超级粉丝。然而,在使用hooks的时候有一些奇怪的限制。在这里,我将为那些对于理解这些规则有困难的人,呈现一个模型来解释如何去思考新的API。数组
我听闻有些人对于新的hooks API的“魔力”感到很是困惑,所以我想我会至少在使用级别揭秘这个语法是怎么工做的。bash
对于hooks的使用,React核心团队规定了两条使用规则,这两条规则列在了文档Hooks规则中。函数
后一条规则,我想是显而易见的。为了将这些功能添加到(React)函数组件中,就必需要将这些功能与(React)组件联系起来。 然而,前一条规则,我想多是比较使人疑惑的。由于对于使用一个像这样的API来讲,这条规则彷佛不太常见。这也正是,我今天想要探索内容。ui
为了获取到一个更加形象的模型,让咱们来看下hooks API的一个简单的使用是什么样的。spa
请注意这只是猜想,也只是使用API的一种方式,用来展示关于hooks,你可能想知道的。这并不必定是API内部如何真正工做的。插件
useState()
?让咱们在这里演示一个例子,来论证下state hook的可能的工做方式。code
首先让咱们从一个组件开始:cdn
function RenderFunctionComponent() {
const [firstName, setFirstName] = useState('Rudi');
const [lastName, setLastName] = useState('Yardley');
return (
<Button onClick={() => setFirstName('Fred')}>Fred</Button>
);
}
复制代码
在hooks API背后的思想是:你可使用一个setter函数做为hook函数返回值的数组的第二个元素。这个setter函数会控制被hook管理的state状态值。
让咱们来解释下这些在React的内部多是怎么工做的。接下来的执行过程是在渲染一个特定组件的执行上下文中运行。这意味着这里存储的数据在渲染组件的外级存放。这个state不与其余组件共享,只维护在一个做用域中,在这个特定组件二次渲染的时候能够获取获得。
建立两个空的数组:setters
和state
设置光标为0:
第一次运行这个函数组件。 每一个useState
调用,首次运行的时候,都会将一个setter函数(绑定着一个光标位置)推动setters
数组中,而后将一些state状态值推动state
数组。
每一个二次渲染,光标都会被重制,那些值正是从每个数组中去读取。
每个setter跟它的光标位置之间都有一个对应关系。所以任一一个setter被触发调用,在state数组中,相应位置的state的值就会被改变。
这里是一段代码示例来论证执行过程。
let state = [];
let setters = [];
let firstRun = true;
let cursor = 0;
function createSetter(cursor) {
return function setterWithCursor(newVal) {
state[cursor] = newVal;
}
}
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];
}
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>
)
}
function MyComponent() {
cursor = 0; // 重置光标
return <RenderFunctionComponent />; // 渲染
}
console.log(state); // 渲染以前:[]
MyComponent();
console.log(state); // 首次渲染: ['Rudi', 'Yardley']
MyComponent();
console.log(state); // 再次渲染: ['Rudi', 'Yardley']
// 点击'Fred'按钮
console.log(state); // 在点击之后:['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
,由于咱们的state的存储变得不一致了。这显然是错误的、而且不能正常工做的。可是这也给了咱们一个解释,为何hooks的原则要被设置成那样。
因此如今应该很明确了,为何你不能在条件或者是循环中调用hooks。由于咱们正在处理一个指向一系列数组的光标。若是你在render之间改变调用的顺序,光标将没法与数据相匹配,同时你的use调用将不会指向正确的数据或者是处理器。
因此这里的技巧是设想hooks是把它的功能逻辑看成一系列数组来处理,同时还须要一个可以保持一致性的光标。若是你能作到这一点,全部的hooks都将能正常工做。
但愿我已经呈现了一个比较清晰的思惟模型,可以帮助你们思考在新的Hooks API下,一切都是怎么运行的。记得这里(hooks)真正的价值是可以将一组相关的逻辑组合在一块儿,所以对于执行顺序要当心。使用hooks API将会有较高的回报。
Hooks对于React组件是一个有用的API插件。这也是人们对于hooks感到比较激动的一个缘由。若是你将这种模型想象成一系列的存放state状态值的数组,那么你应该不会在使用他们的时候违反这些规则。