做者: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狀態值的數組,那麼你應該不會在使用他們的時候違反這些規則。