(譯)React hooks:它不是一種魔法,只是一個數(shù)組——使用圖表揭秘提案規(guī)則.MarkDown

原文地址:https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e

譯文:染陌 (Github

譯文地址:https://github.com/answershuto/Blog

轉(zhuǎn)載請著名出處

我是一名hooks API的忠實(shí)粉絲跟匆,然而它對你的使用會有一些奇怪的約束用押,所以我在本文中使用一個模型來把原理展示給那些想去使用新的API卻難以理解它的規(guī)則的人。

警告:Hooks 還處于實(shí)驗(yàn)階段

本文提到的 Hooks API 還處于實(shí)驗(yàn)階段,如果你需要的是穩(wěn)定的 React API 文檔纤泵,可以從這里找到。

解密 Hooks 的工作方式

我發(fā)現(xiàn)一些同學(xué)苦苦思索新的 Hooks API 中的“魔法”镜粤,所以我打算嘗試著去解釋一下捏题,至少從表層出發(fā),它是如何工作的肉渴。

Hooks 的規(guī)則

React 核心團(tuán)隊(duì)在Hooks的提案中提出了兩個在你使用Hooks的過程中必須去遵守的主要規(guī)則公荧。

  • 請不要在循環(huán)、條件或者嵌套函數(shù)中調(diào)用 Hooks
  • 都有在 React 函數(shù)中才去調(diào)用 Hooks

后者我覺得是顯而易見的同规,你需要用函數(shù)的方式把行為與組件關(guān)聯(lián)起來才能把行為添加到組件循狰。

然而對于前者,我認(rèn)為它會讓人產(chǎn)生困惑券勺,因?yàn)檫@樣使用 API 編程似乎顯得不那么自然绪钥,但這就是我今天要套索的內(nèi)容。

Hooks 的狀態(tài)管理都是依賴數(shù)組的

為了讓大家產(chǎn)生一個更清晰的模型关炼,讓我們來看一下 Hooks 的簡單實(shí)現(xiàn)可能是什么樣子程腹。

需要注意的是,這部分內(nèi)容只是 API 的一種可能實(shí)現(xiàn)方法儒拂,以便讀者更好地趣理解它寸潦。它并不是 API 實(shí)際在內(nèi)部的工作方式,而且它只是一個提案社痛,在未來都會有可能發(fā)生變化见转。

我們應(yīng)該如何實(shí)現(xiàn)“useState()”呢?

讓我們通過一個例子來理解狀態(tài)可能是如何工作的蒜哀。

首先讓我們從一個組件開始:

代碼地址

/* 譯: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 函數(shù)通過 Hook 函數(shù)的第二個參數(shù)返回斩箫,用該函數(shù)來控制 Hook 管理的壯體。

所以 React 能用這個做什么呢?

首先讓我們解釋一下它在 React 內(nèi)部是如何工作的校焦。在執(zhí)行上下文去渲染一個特殊組件的時候赊抖,下面這些步驟會被執(zhí)行。這意味著寨典,數(shù)據(jù)的存儲是獨(dú)立于組件之外的氛雪。該狀態(tài)不能與其他組件共享,但是它擁有一個獨(dú)立的作用域耸成,在該作用域需要被渲染時讀取數(shù)據(jù)报亩。

(1)初始化

創(chuàng)建兩個空數(shù)組“setters”與“state”

設(shè)置指針“cursor”為 0

image

(2)首次渲染

首次執(zhí)行組件函數(shù)

每當(dāng) useState() 被調(diào)用時,如果它是首次渲染井氢,它會通過 push 將一個 setter 方法(綁定了指針“cursor”位置)放進(jìn) setters 數(shù)組中弦追,同時,也會將另一個對應(yīng)的狀態(tài)放進(jìn) state 數(shù)組中去花竞。

image

(3)后續(xù)渲染

每次的后續(xù)渲染都會重置指針“cursor”的位置劲件,并會從每個數(shù)組中讀取對應(yīng)的值。

image

(4)處理事件

每個 setter 都會有一個對應(yīng)的指針位置的引用约急,因此當(dāng)觸發(fā)任何 setter 調(diào)用的時候都會觸發(fā)去改變狀態(tài)數(shù)組中的對應(yīng)的值零远。

image

以及底層的實(shí)現(xiàn)

這是一段示例代碼:

代碼地址

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']

為什么說順序很重要呢?

如果我們基于一些外部條件或是說組件的狀態(tài)去改變 Hooks 在渲染周期的順序厌蔽,那會發(fā)生什么呢牵辣?

讓我們做一些 React 團(tuán)隊(duì)禁止去做的事情。

代碼地址

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>
  );
}

我們在條件語句中調(diào)用了 useState 函數(shù)奴饮,讓我們看看它對整個系統(tǒng)造成的破壞纬向。

糟糕組件的首次渲染

image

到此為止,我們的變量 firstName 與 lastName 依舊包含了正確的數(shù)據(jù)戴卜,讓我們繼續(xù)去看一下第二次渲染會發(fā)生什么事情逾条。

糟糕的第二次渲染

image

現(xiàn)在 firstName 與 lastName 這兩個變量全部被設(shè)置為“Rudi”,與我們實(shí)際的存儲狀態(tài)不符投剥。

這個例子的用法顯然是不正確的师脂,但是它讓我們知道了為什么我們必須使用 React 團(tuán)隊(duì)規(guī)定的規(guī)則去使用 Hooks。

React 團(tuán)隊(duì)制定了這個規(guī)則薇缅,是因?yàn)槿绻蛔裱@套規(guī)則去使用 Hooks API會導(dǎo)致數(shù)據(jù)有問題。

思考 Hooks 維護(hù)了一些列的數(shù)組攒磨,所以你不應(yīng)該去違反這些規(guī)則

所以你現(xiàn)在應(yīng)該清除為什么你不應(yīng)該在條件語句或者循環(huán)語句中使用 Hooks 了泳桦。因?yàn)槲覀兙S護(hù)了一個指針“cursor”指向一個數(shù)組,如果你改變了 render 函數(shù)內(nèi)部的調(diào)用順序娩缰,那么這個指針“cursor”將不會匹配到正確的數(shù)據(jù)灸撰,你的調(diào)用也將不會指向正確的數(shù)據(jù)或句柄。

因此,有一個訣竅就是你需要思考 Hooks 作為一組需要一個匹配一致的指針“cursor”去管理的數(shù)組(染陌譯)浮毯。如果做到了這一點(diǎn)完疫,那么采用任何的寫法它都可以正常工作。

總結(jié)

希望通過上述的講解债蓝,我已經(jīng)給大家建立了一個關(guān)于 Hooks 的更加清晰的思維模型壳鹤,以此可以去思考新的 Hooks API 底層到底做了什么事情。請記住饰迹,它真正的價值在于能夠關(guān)注點(diǎn)聚集在一起芳誓,同時小心它的順序,那使用 Hooks API 會很高的回報(bào)啊鸭。

Hooks 是 React 組件的一個很有用的插件锹淌,這也佐證了為何大家為何對此感到如此興奮。如果你腦海中形成了我上述的這種思維模型赠制,把這種狀態(tài)作為一組數(shù)組的存在赂摆,那么你就會發(fā)現(xiàn)在使用中不會打破它的規(guī)則。

我希望將來再去研究一下 useEffects useEffects 方法钟些,并嘗試將其與 React 的生命周期進(jìn)行比較烟号。

這篇文章是一篇在線文檔,如果你想要參與貢獻(xiàn)或者有任何有誤的地方厘唾,歡迎聯(lián)系我褥符。

你可以在 Twitter 上面 fllow 我(Rudi Yardley)或者在Github找到我。

染陌 譯:https://github.com/answershuto

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抚垃,一起剝皮案震驚了整個濱河市喷楣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鹤树,老刑警劉巖铣焊,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異罕伯,居然都是意外死亡曲伊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門追他,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坟募,“玉大人,你說我怎么就攤上這事邑狸⌒概矗” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵单雾,是天一觀的道長赚哗。 經(jīng)常有香客問我她紫,道長,這世上最難降的妖魔是什么屿储? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任贿讹,我火速辦了婚禮,結(jié)果婚禮上够掠,老公的妹妹穿的比我還像新娘民褂。我一直安慰自己,他們只是感情好祖屏,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布助赞。 她就那樣靜靜地躺著,像睡著了一般袁勺。 火紅的嫁衣襯著肌膚如雪雹食。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天期丰,我揣著相機(jī)與錄音群叶,去河邊找鬼。 笑死钝荡,一個胖子當(dāng)著我的面吹牛街立,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播埠通,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼赎离,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了端辱?” 一聲冷哼從身側(cè)響起梁剔,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎舞蔽,沒想到半個月后荣病,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡渗柿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年个盆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朵栖。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡颊亮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出陨溅,到底是詐尸還是另有隱情终惑,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布声登,位于F島的核電站狠鸳,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏悯嗓。R本人自食惡果不足惜件舵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望脯厨。 院中可真熱鬧铅祸,春花似錦、人聲如沸合武。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稼跳。三九已至盟庞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間汤善,已是汗流浹背什猖。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留红淡,地道東北人不狮。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像在旱,于是被迫代替她去往敵國和親摇零。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容