react hook -- 自定義Hook

當我們想在兩個函數(shù)之間共享邏輯時吠昭,我們會把它提取到第三個函數(shù)中喊括。而組件和Hook都是函數(shù)矢棚,所以也同樣適用這種方式。

認識自定義Hook

自定義Hook是一個函數(shù)蒲肋,其名稱以use開頭,函數(shù)內部可以調用其他Hook兜粘。

在自定義Hook的頂層可以無條件地調用其他Hook(useState, useEffect)申窘。
我們可以自由決定自定義Hook的參數(shù)返回值孔轴。
自定義Hook必須以use開頭,這樣方便判斷該函數(shù)內部是否調用了內部Hook路鹰,以及React能自動檢查Hook是否違反了Hook的規(guī)則(見Hook規(guī)則部分)。
每次使用自定義Hook時悍引,其中的state和副作用都是完全隔離的恩脂。

Hooks 和普通函數(shù)在語義上是有區(qū)別的趣斤,就在于函數(shù)中有沒有用到其它 Hooks。

就是說如果你創(chuàng)建了一個 useXXX 的函數(shù)浓领,但是內部并沒有用任何其它 Hooks,那么這個函數(shù)就不是一個 Hook联贩,而只是一個普通的函數(shù)漫仆。但是如果用了其它 Hooks 泪幌,那么它就是一個 Hook构韵。

自定義Hook的特點

  • 名字一定是以 use 開頭的函數(shù)曹抬,這樣 React 才能夠知道這個函數(shù)是一個 Hook蜗元。
  • 函數(shù)內部一定調用了其它的 Hooks,可以是內置的 Hooks届宠,也可以是其它自定義 Hooks嘹锁。這樣才能夠讓組件刷新,或者去產(chǎn)生副作用赶熟。

可重用邏輯直接寫一個工具類不就行了嗎?為什么一定要通過Hook進行封裝呢映砖?

因為在 Hooks 中,你可以管理當前組件的 state罩旋,從而將更多的邏輯寫在可重用的 Hooks 中眶诈。但是要知道涨醋,在普通的工具類中是無法直接修改組件 state 的逝撬,那么也就無法在數(shù)據(jù)改變的時候觸發(fā)組件的重新渲染。

拆分邏輯的目的不一定是為了重用宪潮,而可以是僅僅為了業(yè)務邏輯的隔離。

在這個場景下狡相,我們不一定要把 Hooks 放到獨立的文件中梯轻,而是可以和函數(shù)組件寫在一個文件中尽棕。這么做的原因就在于,這些 Hooks 是和當前函數(shù)組件緊密相關的滔悉,所以寫到一起,反而更容易閱讀和理解回官。

例:

function MyComponent() {
  const [id, setId] = useState(1);
  const isOnline = useOnlineStatus(id);

  return (
    <>
    // other nodes
    </>
  )
}

useState為我們提供了id的最新值曹宴,并把它做為參數(shù)傳入useOnlineStatus, 當id改變時歉提,useOnlineStatus Hook會取消訂閱前一個id,并訂閱新的id苔巨。

例一:自定義 Hook 處理 LocalStorage 的存取

需求:希望把一些數(shù)據(jù)存儲到 localStorage 中 - 不使用自定義Hook

不使用自定義Hook

import React, { useState, useEffect } from 'react'

export default function CustomDataStoreHook() {
  const [name, setName] = useState(() => {
    return JSON.parse(window.localStorage.getItem("name"))
  });

  useEffect(() => {
    window.localStorage.setItem("name", JSON.stringify(name));
  }, [name])

  return (
    <div>
      <h2>CustomDataStoreHook: {name}</h2>
      <button onClick={e => setName("gercke")}>設置name</button>
    </div>
  )
}

定義自定義Hook - useLocalStorage

import React,{useState, useEffect} from 'react';
function useLocalStorage(key) {
  const [data, setData] = useState(() => {
    return JSON.parse(window.localStorage.getItem(key))
  });

  useEffect(() => {
    window.localStorage.setItem(key, JSON.stringify(data));
  }, [data]);

  return [data, setData];
}

export default useLocalStorage;

使用自定義Hook

import React, { useState, useEffect } from 'react';

import useLocalStorage from '../hooks/local-store-hook';

export default function CustomDataStoreHook() {
  const [name, setName] = useLocalStorage("name");

  return (
    <div>
      <h2>CustomDataStoreHook: {name}</h2>
      <button onClick={e => setName("kobe")}>設置name</button>
    </div>
  )
}

例二:自定義Hook監(jiān)聽瀏覽器狀態(tài)變化

需求:當 y > 300 時,顯示Back to top按鈕

定義自定義Hook - useScroll

import { useState, useEffect } from 'react';

// 獲取橫向恋拷,縱向滾動條位置
const getPosition = () => {
  return {
    x: document.body.scrollLeft,
    y: document.body.scrollTop,
  };
};
const useScroll = () => {
  // 定一個 position 這個 state 保存滾動條位置
  const [position, setPosition] = useState(getPosition());
  useEffect(() => {
    const handler = () => {
      setPosition(getPosition(document));
    };
    // 監(jiān)聽 scroll 事件,更新滾動條位置
    document.addEventListener("scroll", handler);
    return () => {
      // 組件銷毀時蔬顾,取消事件監(jiān)聽
      document.removeEventListener("scroll", handler);
    };
  }, []);
  return position;
};

使用自定義Hook

import React, { useCallback } from 'react';
import useScroll from './useScroll';

function ScrollTop() {
  const { y } = useScroll();

  const goTop = useCallback(() => {
    document.body.scrollTop = 0;
  }, []);

  const style = {
    position: "fixed",
    right: "10px",
    bottom: "10px",
  };
  // 當滾動條位置縱向超過 300 時湘捎,顯示返回頂部按鈕
  if (y > 300) {
    return (
      <button onClick={goTop} style={style}>
        Back to Top
      </button>
    );
  }
  // 否則不 render 任何 UI
  return null;
}

Hook 規(guī)則

  • 只在最頂層使用Hook
    • 不要在循環(huán)、條件或嵌套中調用Hook
  • 只在React函數(shù)中調用Hook
    • 在react的函數(shù)組件中調用Hook
    • 在自定義Hook中調用其他Hook

要確保Hook的調用順序在每次渲染中都是相同的

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末窥妇,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子娩践,更是在濱河造成了極大的恐慌,老刑警劉巖翻伺,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吨岭,居然都是意外死亡拉宗,警方通過查閱死者的電腦和手機辣辫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來急灭,“玉大人,你說我怎么就攤上這事葬馋÷衾穑” “怎么了点楼?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長掠廓。 經(jīng)常有香客問我,道長蟀瞧,這世上最難降的妖魔是什么沉颂? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任悦污,我火速辦了婚禮,結果婚禮上切端,老公的妹妹穿的比我還像新娘彻坛。我一直安慰自己,他們只是感情好钙蒙,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著间驮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪竞帽。 梳的紋絲不亂的頭發(fā)上扛施,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天疙渣,我揣著相機與錄音,去河邊找鬼抱虐。 笑死饥脑,一個胖子當著我的面吹牛恳邀,可吹牛的內容都是我干的。 我是一名探鬼主播谣沸,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼笋颤!你這毒婦竟也來了?” 一聲冷哼從身側響起伴澄,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎非凌,沒想到半個月后举农,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體敞嗡,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年喉悴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箕肃。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡婚脱,死狀恐怖,靈堂內的尸體忽然破棺而出涡贱,到底是詐尸還是另有隱情,我是刑警寧澤惹想,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站嘀粱,受9級特大地震影響激挪,放射性物質發(fā)生泄漏锋叨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一娃磺、第九天 我趴在偏房一處隱蔽的房頂上張望薄湿。 院中可真熱鬧偷卧,春花似錦、人聲如沸听诸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至仔蝌,卻和暖如春泛领,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背渊鞋。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留豆混,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓皿伺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親鸵鸥。 傳聞我的和親對象是個殘疾皇子奠滑,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容