Hook 是 React 16.8 的新增特性逐抑。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性裆装。
官方demo
import React, { useState } from 'react';
function Example() {
// 聲明一個(gè)新的叫做 “count” 的 state 變量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
動(dòng)機(jī)
Hook 解決了我們五年來(lái)編寫和維護(hù)成千上萬(wàn)的組件時(shí)遇到的各種各樣看起來(lái)不相關(guān)的問(wèn)題溜族。無(wú)論你正在學(xué)習(xí) React浓体,或每天使用,或者更愿嘗試另一個(gè)和 React 有相似組件模型的框架,你都可能對(duì)這些問(wèn)題似曾相識(shí)助析。
在組件之間復(fù)用狀態(tài)邏輯很難
React 沒(méi)有提供將可復(fù)用性行為“附加”到組件的途徑(例如犀被,把組件連接到 store)。如果你使用過(guò) React 一段時(shí)間外冀,你也許會(huì)熟悉一些解決此類問(wèn)題的方案寡键,比如 render props 和 高階組件。但是這類方案需要重新組織你的組件結(jié)構(gòu)雪隧,這可能會(huì)很麻煩西轩,使你的代碼難以理解。如果你在 React DevTools 中觀察過(guò) React 應(yīng)用脑沿,你會(huì)發(fā)現(xiàn)由 providers遭商,consumers,高階組件捅伤,render props 等其他抽象層組成的組件會(huì)形成“嵌套地獄”。盡管我們可以在 DevTools 過(guò)濾掉它們巫玻,但這說(shuō)明了一個(gè)更深層次的問(wèn)題:React 需要為共享狀態(tài)邏輯提供更好的原生途徑丛忆。
你可以使用 Hook 從組件中提取狀態(tài)邏輯,使得這些邏輯可以單獨(dú)測(cè)試并復(fù)用仍秤。Hook 使你在無(wú)需修改組件結(jié)構(gòu)的情況下復(fù)用狀態(tài)邏輯熄诡。 這使得在組件間或社區(qū)內(nèi)共享 Hook 變得更便捷。
具體將在自定義 Hook 中對(duì)此展開(kāi)更多討論诗力。
復(fù)雜組件變得難以理解
我們經(jīng)常維護(hù)一些組件凰浮,組件起初很簡(jiǎn)單,但是逐漸會(huì)被狀態(tài)邏輯和副作用充斥苇本。每個(gè)生命周期常常包含一些不相關(guān)的邏輯袜茧。例如,組件常常在 componentDidMount
和 componentDidUpdate
中獲取數(shù)據(jù)瓣窄。但是笛厦,同一個(gè) componentDidMount
中可能也包含很多其它的邏輯,如設(shè)置事件監(jiān)聽(tīng)俺夕,而之后需在 componentWillUnmount
中清除裳凸。相互關(guān)聯(lián)且需要對(duì)照修改的代碼被進(jìn)行了拆分,而完全不相關(guān)的代碼卻在同一個(gè)方法中組合在一起劝贸。如此很容易產(chǎn)生 bug姨谷,并且導(dǎo)致邏輯不一致。
在多數(shù)情況下映九,不可能將組件拆分為更小的粒度梦湘,因?yàn)闋顟B(tài)邏輯無(wú)處不在。這也給測(cè)試帶來(lái)了一定挑戰(zhàn)。同時(shí)践叠,這也是很多人將 React 與狀態(tài)管理庫(kù)結(jié)合使用的原因之一言缤。但是,這往往會(huì)引入了很多抽象概念禁灼,需要你在不同的文件之間來(lái)回切換管挟,使得復(fù)用變得更加困難。
為了解決這個(gè)問(wèn)題弄捕,Hook 將組件中相互關(guān)聯(lián)的部分拆分成更小的函數(shù)(比如設(shè)置訂閱或請(qǐng)求數(shù)據(jù))僻孝,而并非強(qiáng)制按照生命周期劃分。你還可以使用 reducer 來(lái)管理組件的內(nèi)部狀態(tài)守谓,使其更加可預(yù)測(cè)穿铆。
我們將在使用 Effect Hook 中對(duì)此展開(kāi)更多討論。
難以理解的 class
除了代碼復(fù)用和代碼管理會(huì)遇到困難外斋荞,我們還發(fā)現(xiàn) class 是學(xué)習(xí) React 的一大屏障荞雏。你必須去理解 JavaScript 中 this
的工作方式,這與其他語(yǔ)言存在巨大差異平酿。還不能忘記綁定事件處理器凤优。沒(méi)有穩(wěn)定的語(yǔ)法提案,這些代碼非常冗余蜈彼。大家可以很好地理解 props筑辨,state 和自頂向下的數(shù)據(jù)流,但對(duì) class 卻一籌莫展幸逆。即便在有經(jīng)驗(yàn)的 React 開(kāi)發(fā)者之間棍辕,對(duì)于函數(shù)組件與 class 組件的差異也存在分歧,甚至還要區(qū)分兩種組件的使用場(chǎng)景还绘。
另外楚昭,React 已經(jīng)發(fā)布五年了,我們希望它能在下一個(gè)五年也與時(shí)俱進(jìn)拍顷。就像 Svelte哪替,Angular,Glimmer等其它的庫(kù)展示的那樣菇怀,組件預(yù)編譯會(huì)帶來(lái)巨大的潛力凭舶。尤其是在它不局限于模板的時(shí)候。最近爱沟,我們一直在使用 Prepack 來(lái)試驗(yàn) component folding帅霜,也取得了初步成效。但是我們發(fā)現(xiàn)使用 class 組件會(huì)無(wú)意中鼓勵(lì)開(kāi)發(fā)者使用一些讓優(yōu)化措施無(wú)效的方案呼伸。class 也給目前的工具帶來(lái)了一些問(wèn)題身冀。例如钝尸,class 不能很好的壓縮,并且會(huì)使熱重載出現(xiàn)不穩(wěn)定的情況搂根。因此珍促,我們想提供一個(gè)使代碼更易于優(yōu)化的 API。
為了解決這些問(wèn)題剩愧,Hook 使你在非 class 的情況下可以使用更多的 React 特性猪叙。 從概念上講,React 組件一直更像是函數(shù)仁卷。而 Hook 則擁抱了函數(shù)穴翩,同時(shí)也沒(méi)有犧牲 React 的精神原則。Hook 提供了問(wèn)題的解決方案锦积,無(wú)需學(xué)習(xí)復(fù)雜的函數(shù)式或響應(yīng)式編程技術(shù)芒帕。
示例
Hook 概覽是開(kāi)始學(xué)習(xí) Hook 的不錯(cuò)選擇。
漸進(jìn)策略
總結(jié):沒(méi)有計(jì)劃從 React 中移除 class丰介。
大部分 React 開(kāi)發(fā)者會(huì)專注于開(kāi)發(fā)產(chǎn)品背蟆,而沒(méi)時(shí)間關(guān)注每一個(gè)新 API 的發(fā)布。Hook 還很新哮幢,也許等到有更多示例和教程后淆储,再考慮學(xué)習(xí)或使用它們也不遲。
我們也明白向 React 添加新的原生概念的門檻非常高家浇。我們?yōu)楹闷娴淖x者準(zhǔn)備了詳細(xì)的征求意見(jiàn)文檔,在文檔中用更多細(xì)節(jié)深入討論了我們推進(jìn)這件事的動(dòng)機(jī)碴裙,也在具體設(shè)計(jì)決策和相關(guān)先進(jìn)技術(shù)上提供了額外的視角钢悲。
最重要的是,Hook 和現(xiàn)有代碼可以同時(shí)工作舔株,你可以漸進(jìn)式地使用他們莺琳。 不用急著遷移到 Hook。我們建議避免任何“大規(guī)模重寫”载慈,尤其是對(duì)于現(xiàn)有的惭等、復(fù)雜的 class 組件。開(kāi)始“用 Hook 的方式思考”前办铡,需要做一些思維上的轉(zhuǎn)變辞做。按照我們的經(jīng)驗(yàn),最好先在新的不復(fù)雜的組件中嘗試使用 Hook寡具,并確保團(tuán)隊(duì)中的每一位成員都能適應(yīng)秤茅。在你嘗試使用 Hook 后,歡迎給我們提供反饋童叠,無(wú)論好壞框喳。
我們準(zhǔn)備讓 Hook 覆蓋所有 class 組件的使用場(chǎng)景,但是我們將繼續(xù)為 class 組件提供支持。在 Facebook五垮,我們有成千上萬(wàn)的組件用 class 書寫乍惊,我們完全沒(méi)有重寫它們的計(jì)劃市埋。相反佩耳,我們開(kāi)始在新的代碼中同時(shí)使用 Hook 和 class。
FAQ
我們準(zhǔn)備了 Hooks FAQ 來(lái)解答最常見(jiàn)的關(guān)于 Hook 的問(wèn)題书幕。
下一步
在本章節(jié)的最后匙监,你應(yīng)該對(duì) Hook 能解決什么問(wèn)題有了粗略的理解凡橱,但可能還有許多細(xì)節(jié)不清楚。不要擔(dān)心亭姥!讓我們?nèi)?a target="_blank">下一章節(jié)通過(guò)例子學(xué)習(xí) Hook稼钩。