原文地址:https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236ejavascript
譯文:染陌 (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找到我。