【hooks】React-Hooks詳解

Hooks的作用

在沒有hooks之前吓笙,函數(shù)組件只能夠接收props 渲染UI舵变,以及觸發(fā)父組件傳過來的事件客峭。所有的邏輯都要在類組件中實(shí)現(xiàn)。這樣會使類組件內(nèi)部錯綜復(fù)雜矩屁,每一個類組件都有一套獨(dú)特的狀態(tài)辟宗,相互之間不能復(fù)用。即便React之前出現(xiàn)過mixins等方案吝秕,但是minxins會伴隨著隱式依賴和代碼沖突等問題慢蜓。
另外函數(shù)組件要比類組件輕巧很多,同時更加靈活

hooks的目的:

  • 讓函數(shù)組件也能做類組件的事情郭膛,有自己的狀態(tài),可以處理一些副作用氛悬,能獲取ref则剃,也能做數(shù)據(jù)緩存
  • 解決邏輯復(fù)用的問題
  • 放棄面向?qū)ο缶幊?擁抱函數(shù)式編程

常見的hooks

數(shù)據(jù)驅(qū)動型:

  • useState
    管理組件的狀態(tài),適用于簡單狀態(tài)的管理如捅。
const [state, setState] = useState(initialState);
  • useReducer
    用于管理復(fù)雜的狀態(tài)邏輯或涉及多個子狀態(tài)的情況棍现,替代 useState 的一種方式。
const [state, dispatch] = useReducer(reducer, initialState);

狀態(tài)獲取/傳遞型:

  • useContext
    用于獲取上層組件通過 Context 提供的全局狀態(tài)镜遣,避免層層傳遞 props己肮。
const value = useContext(MyContext);
  • useRef
    用于獲取和存儲 DOM 節(jié)點(diǎn)或其他持久值,組件重新渲染時不會改變悲关。
const ref = useRef(initialValue);
  • useImperativeHandle
    與 forwardRef 配合使用谎僻,允許自定義暴露給父組件的實(shí)例值。
import React, { useImperativeHandle, forwardRef, useRef } from 'react';

// Step 1: 使用 forwardRef 包裹子組件
const CustomInput = forwardRef((props, ref) => {
  const inputRef = useRef();

  // Step 2: 使用 useImperativeHandle 暴露自定義方法
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
    clear: () => {
      inputRef.current.value = '';
    }
  }));

  return <input ref={inputRef} {...props} />;
});

// 父組件
export default function ParentComponent() {
  const inputRef = useRef();

  return (
    <div>
      <CustomInput ref={inputRef} placeholder="Type here..." />
      <button onClick={() => inputRef.current.focus()}>Focus Input</button>
      <button onClick={() => inputRef.current.clear()}>Clear Input</button>
    </div>
  );
}

副作用型:

  • useEffect
    用于處理副作用(例如數(shù)據(jù)獲取寓辱、訂閱艘绍、手動修改 DOM 等)。它在組件渲染后執(zhí)行秫筏,依賴數(shù)組變化時觸發(fā)诱鞠。
useEffect(() => {
    // Effect
    return () => {
        // Cleanup
    };
}, [dependencies]);
  • useLayoutEffect
    與 useEffect 類似挎挖,但在所有 DOM 變更后同步執(zhí)行,適用于需要在繪制前進(jìn)行 DOM 操作的情況航夺。
useLayoutEffect(() => {
    // Effect
    return () => {
        // Cleanup
    };
}, [dependencies]);

  • useInsertionEffect
    用于在 CSS-in-JS 庫中注入樣式蕉朵。它在 DOM 元素渲染之前同步運(yùn)行,通常比 useLayoutEffect 更早阳掐,可以避免布局抖動
useInsertionEffect(() => {
    // 樣式注入邏輯
    return () => {
        // 清除邏輯
    };
}, [dependencies]);

狀態(tài)派生/狀態(tài)保存

  • useMemo
    用于緩存計算結(jié)果始衅,僅在依賴變化時重新計算,優(yōu)化性能锚烦。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  • useCallback
    用于緩存函數(shù)觅闽,使其在依賴未變更的情況下不會重新創(chuàng)建,適用于傳遞給子組件的回調(diào)函數(shù)涮俄。
const memoizedCallback = useCallback(() => {
    doSomething(a, b);
}, [a, b]);

React 18新增的hooks

  • useSyncExternalStore

  • useTranstion

  • useDeferredValue

  • useInsertionEffect

  • useId

除此之外React 18還新增了 concurrent模式
concurrent模式指的是在 React 18 中蛉拙,組件的渲染過程可以同時進(jìn)行多個任務(wù),而不僅僅是一個任務(wù)彻亲。
提高應(yīng)用的性能和用戶體驗(yàn)孕锄。它使 React 能夠在更新 UI 時更靈活地管理渲染資源,從而有效應(yīng)對復(fù)雜界面的卡頓問題苞尝。Concurrent 模式引入了一種“打斷式渲染”的機(jī)制畸肆,使 React 可以根據(jù)任務(wù)的重要性和優(yōu)先級分配渲染時間

  • 可中斷渲染:在傳統(tǒng)的同步渲染中,一次渲染過程是不可中斷的宙址,一旦開始轴脐,整個更新必須完成。在 Concurrent 模式中抡砂,渲染過程是可中斷的大咱。React 可以在合適的時候暫停或放棄某些任務(wù)注益,讓高優(yōu)先級的任務(wù)(如用戶輸入碴巾、動畫)優(yōu)先渲染。

  • 時間分片(Time Slicing):Concurrent 模式將渲染任務(wù)分割成小塊丑搔,并在空閑時逐步執(zhí)行這些小塊厦瓢。通過這種方式,React 可以在處理復(fù)雜渲染的同時保持頁面的響應(yīng)性啤月,不會因?yàn)殇秩竞臅r任務(wù)而出現(xiàn)卡頓煮仇。

  • 自動調(diào)節(jié)優(yōu)先級:React 能夠根據(jù)任務(wù)的類型動態(tài)調(diào)整優(yōu)先級。例如谎仲,當(dāng)用戶與應(yīng)用交互時欺抗,React 會自動將用戶輸入的處理設(shè)為高優(yōu)先級,并暫緩其他不那么緊急的渲染任務(wù)强重。這種動態(tài)調(diào)整確保了應(yīng)用能夠在不同情況下保持流暢绞呈。

優(yōu)點(diǎn):

  • 更流暢的用戶體驗(yàn):減少大規(guī)模渲染任務(wù)帶來的卡頓贸人,使動畫和交互更加平滑。

  • 優(yōu)先級管理:高優(yōu)先級任務(wù)(如用戶輸入)能夠迅速響應(yīng)佃声,而非阻塞在渲染隊(duì)列中艺智。

  • 更智能的資源分配:復(fù)雜組件在渲染時會自動優(yōu)化渲染順序,提高頁面性能

React 的 Concurrent 模式在底層通過 ReactDOM.createRoot 實(shí)現(xiàn)圾亏,并且是向后兼容的十拣。只需使用 ReactDOM.createRoot 代替?zhèn)鹘y(tǒng)的 ReactDOM.render

import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

useEffect 與 useLayoutEffect 區(qū)別

執(zhí)行時機(jī):
useEffect:在瀏覽器完成繪制之后執(zhí)行。這意味著 React 會先更新 DOM 和頁面布局志鹃,然后執(zhí)行 useEffect 內(nèi)的副作用夭问。這是異步執(zhí)行的,通常不會阻塞頁面的繪制曹铃。

useLayoutEffect:在 DOM 更新后缰趋、瀏覽器繪制之前執(zhí)行。它會阻塞瀏覽器的繪制陕见,確保在布局和繪制之前完成所有同步的 DOM 操作秘血。這是同步執(zhí)行的,適合需要在渲染前計算布局或進(jìn)行 DOM 操作的情況评甜。

用途:
useEffect:適用于不影響布局的副作用灰粮,例如數(shù)據(jù)獲取、事件監(jiān)聽忍坷、日志記錄等粘舟。這些操作無需在瀏覽器渲染之前執(zhí)行,因此可以在 DOM 更新后異步執(zhí)行佩研。

useLayoutEffect:適合在瀏覽器繪制前需要直接訪問和操作 DOM 的場景柑肴。例如,讀取或調(diào)整 DOM 元素的位置韧骗、尺寸等,以避免頁面的閃爍或布局跳動零聚。這些操作需要在渲染前完成袍暴。

useMemo 與 useCallback memo 區(qū)別

useMemo:

  • 緩存計算結(jié)果
  • 用于緩存計算結(jié)果,以避免不必要的重復(fù)計算隶症。當(dāng)依賴項(xiàng)沒有變化時政模,useMemo 會返回緩存的值,而不是重新計算蚂会。主要適用于需要進(jìn)行復(fù)雜或昂貴計算的場景淋样。
  • 使用場景:適合計算屬性、生成列表胁住、數(shù)據(jù)過濾等需要緩存結(jié)果的場景趁猴。

useCallback:

  • 緩存函數(shù)引用
  • 用于緩存函數(shù)的引用刊咳,以避免在子組件的 props 變化時觸發(fā)不必要的重渲染。當(dāng)依賴項(xiàng)沒有變化時儡司,useCallback 返回相同的函數(shù)引用娱挨。
  • 使用場景:適合傳遞給子組件的回調(diào)函數(shù),特別是在依賴項(xiàng)不變時捕犬,避免重新創(chuàng)建函數(shù)導(dǎo)致子組件重復(fù)渲染跷坝。

React.memo:緩存組件

  • React.memo 是一個高階組件,用于緩存組件的渲染結(jié)果碉碉。當(dāng)傳遞給組件的 props 沒有變化時柴钻,React.memo 會跳過重新渲染。
  • 適合純展示組件或在 props 不變時無需重新渲染的組件垢粮,以提升性能

使用原則:

  • useMemo:用于避免昂貴的計算操作贴届。
  • useCallback:用于避免回調(diào)函數(shù)引用的變化引起子組件重渲染。
  • React.memo:用于避免在 props 相同的情況下重新渲染組件足丢。

總結(jié):
useMemo 緩存計算結(jié)果 計算后的值 復(fù)雜計算粱腻,避免重復(fù)計算
useCallback 緩存函數(shù)引用 緩存的函數(shù) 避免傳遞給子組件的回調(diào)重復(fù)渲染
React.memo 緩存整個組件 緩存的組件 純展示組件,props 不變時跳過渲染

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斩跌,一起剝皮案震驚了整個濱河市绍些,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耀鸦,老刑警劉巖柬批,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異袖订,居然都是意外死亡氮帐,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門洛姑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來上沐,“玉大人,你說我怎么就攤上這事楞艾〔瘟” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵硫眯,是天一觀的道長蕴侧。 經(jīng)常有香客問我,道長两入,這世上最難降的妖魔是什么净宵? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上择葡,老公的妹妹穿的比我還像新娘紧武。我一直安慰自己,他們只是感情好刁岸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布脏里。 她就那樣靜靜地躺著,像睡著了一般虹曙。 火紅的嫁衣襯著肌膚如雪迫横。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天酝碳,我揣著相機(jī)與錄音矾踱,去河邊找鬼。 笑死疏哗,一個胖子當(dāng)著我的面吹牛呛讲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播返奉,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼贝搁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了芽偏?” 一聲冷哼從身側(cè)響起雷逆,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎污尉,沒想到半個月后膀哲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡被碗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年某宪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锐朴。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡兴喂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出焚志,到底是詐尸還是另有隱情衣迷,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布娩嚼,位于F島的核電站蘑险,受9級特大地震影響滴肿,放射性物質(zhì)發(fā)生泄漏岳悟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贵少。 院中可真熱鬧呵俏,春花似錦、人聲如沸滔灶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽录平。三九已至麻车,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間斗这,已是汗流浹背动猬。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留表箭,地道東北人赁咙。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像免钻,于是被迫代替她去往敵國和親彼水。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

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