React Hooks 淺析

  • React Hooks 的設(shè)計目的兰迫,就是加強版函數(shù)組件,完全不使用"類"漾唉,就能寫出一個全功能的組件。

Hook 這個單詞的意思是"鉤子"堰塌。

React Hooks 的意思是赵刑,組件盡量寫成純函數(shù),如果需要外部功能和副作用场刑,就用鉤子把外部代碼"鉤"進來般此。 React Hooks 就是那些鉤子。
你需要什么功能牵现,就使用什么鉤子铐懊。React 默認(rèn)提供了一些常用鉤子,你也可以封裝自己的鉤子瞎疼。
所有的鉤子都是為函數(shù)引入外部功能科乎,所以 React 約定,鉤子一律使用use前綴命名贼急,便于識別茅茂。你要使用 xxx 功能,鉤子就命名為 usexxx太抓。

下面介紹 React 默認(rèn)提供的四個最常用的鉤子

  • useState()

  • useContext()

  • useReducer()

  • useEffect()


useState():狀態(tài)鉤子

useState()用于為函數(shù)組件引入狀態(tài)(state)空闲。純函數(shù)不能有狀態(tài),所以把狀態(tài)放在鉤子里面腻异。

import React, { useState } from "react";

export default function  Button()  {
  const  [buttonText, setButtonText] =  useState("Click me,   please");

  function handleClick()  {
    return setButtonText("Thanks, been clicked!");
  }

  return  <button  onClick={handleClick}>{buttonText}</button>;
}

上面代碼中进副,Button組件是一個函數(shù),內(nèi)部使用useState()鉤子引入狀態(tài)悔常。

useState()這個函數(shù)接受狀態(tài)的初始值影斑,作為參數(shù),上例的初始值為按鈕的文字机打。該函數(shù)返回一個數(shù)組矫户,數(shù)組的第一個成員是一個變量(上例是buttonText),指向狀態(tài)的當(dāng)前值残邀。第二個成員是一個函數(shù)皆辽,用來更新狀態(tài),約定是set前綴加上狀態(tài)的變量名(上例是setButtonText)芥挣。


useContext():共享狀態(tài)鉤子

如果需要在組件之間共享狀態(tài)驱闷,可以使用useContext()

現(xiàn)在有兩個組件 Navbar 和 Messages空免,我們希望它們之間共享狀態(tài)

<div className="App">
  <Navbar/>
  <Messages/>
</div>

第一步就是使用 React Context API空另,在組件外部建立一個 Context。

const AppContext = React.createContext({});

組件封裝代碼如下:

<AppContext.Provider value={{
  username: 'superawesome'
}}>
  <div className="App">
    <Navbar/>
    <Messages/>
  </div>
</AppContext.Provider>

上面代碼中蹋砚,AppContext.Provider提供了一個 Context 對象扼菠,這個對象可以被子組件共享

Navbar 組件的代碼如下

const Navbar = () => {
  const { username } = useContext(AppContext);
  return (
    <div className="navbar">
      <p>AwesomeSite</p>
      <p>{username}</p>
    </div>
  );
}

上面代碼中摄杂,useContext()鉤子函數(shù)用來引入 Context 對象,從中獲取username屬性

Message 組件的代碼也類似:

const Messages = () => {
  const { username } = useContext(AppContext)

  return (
    <div className="messages">
      <h1>Messages</h1>
      <p>1 message for {username}</p>
      <p className="message">useContext is awesome!</p>
    </div>
  )
}

index.js:

import React, { useContext } from "react";
import ReactDOM from "react-dom";
import "./styles.css";

const AppContext = React.createContext({});

const Navbar = () => {
  const { username } = useContext(AppContext)

  return (
    <div className="navbar">
      <p>AwesomeSite</p>
      <p>{username}</p>
    </div>
  )
}

const Messages = () => {
  const { username } = useContext(AppContext)

  return (
    <div className="messages">
      <h1>Messages</h1>
      <p>1 message for {username}</p>
      <p className="message">useContext is awesome!</p>
    </div>
  )
}

function App() {
  return (
    <AppContext.Provider value={{
      username: 'superawesome'
    }}>
      <div className="App">
        <Navbar />
        <Messages />
      </div>
    </AppContext.Provider>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

useReducer():action 鉤子

React 本身不提供狀態(tài)管理功能循榆,通常需要使用外部庫析恢。這方面最常用的庫是 Redux

Redux 的核心概念是,組件發(fā)出 action 與狀態(tài)管理器通信秧饮。狀態(tài)管理器收到 action 以后映挂,使用 Reducer 函數(shù)算出新的狀態(tài),Reducer 函數(shù)的形式是(state, action) => newState

useReducers()鉤子用來引入 Reducer 功能

const [state, dispatch] = useReducer(reducer, initialState)

上面是useReducer()的基本用法浦楣,它接受 Reducer 函數(shù)和狀態(tài)的初始值作為參數(shù)袖肥,返回一個數(shù)組。數(shù)組的第一個成員是狀態(tài)的當(dāng)前值振劳,第二個成員是發(fā)送 actiondispatch函數(shù)

下面是一個計數(shù)器的例子椎组。用于計算狀態(tài)的 Reducer 函數(shù)如下

const myReducer = (state, action) => {
  switch(action.type)  {
    case('countUp'):
      return  {
        ...state,
        count: state.count + 1
      }
    default:
      return  state;
  }
}

組件代碼如下

function App() {
  const [state, dispatch] = useReducer(myReducer, { count:   0 });
  return  (
    <div className="App">
      <button onClick={() => dispatch({ type: 'countUp' })}>
        +1
      </button>
      <p>Count: {state.count}</p>
    </div>
  );
}

index.js

import React, { useReducer } from "react";
import ReactDOM from "react-dom";
import "./styles.css";

const myReducer = (state, action) => {
  switch(action.type) {
    case('countUp'):
      return {
        ...state,
        count: state.count + 1
      }
    default:
      return state
  }
}

function App() {
  const [state, dispatch] = useReducer(myReducer, { count: 0 })

  return (
    <div className="App">
      <button onClick={() => dispatch({ type: 'countUp' })}>
        +1
      </button>
      <p>Count: {state.count}</p>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
由于 Hooks 可以提供共享狀態(tài)和 Reducer 函數(shù),所以它在這些方面可以取代 Redux历恐。但是寸癌,它沒法提供中間件(middleware)和時間旅行(time travel),如果你需要這兩個功能弱贼,還是要用 Redux

useEffect():副作用鉤子

useEffect()用來引入具有副作用的操作蒸苇,最常見的就是向服務(wù)器請求數(shù)據(jù)。以前吮旅,放在componentDidMount里面的代碼溪烤,現(xiàn)在可以放在useEffect()

useEffect()的用法如下:
useEffect(()  =>  {
  // Async Action
}, [dependencies])

上面用法中,useEffect()接受兩個參數(shù)庇勃。第一個參數(shù)是一個函數(shù)檬嘀,異步操作的代碼放在里面。第二個參數(shù)是一個數(shù)組责嚷,用于給出 Effect 的依賴項鸳兽,只要這個數(shù)組發(fā)生變化,useEffect()就會執(zhí)行罕拂。第二個參數(shù)可以省略揍异,這時每次組件渲染時,就會執(zhí)行useEffect()

const Person = ({ personId }) => {
  const [loading, setLoading] = useState(true);
  const [person, setPerson] = useState({});

  useEffect(() => {
    setLoading(true); 
    fetch(`https://swapi.co/api/people/${personId}/`)
      .then(response => response.json())
      .then(data => {
        setPerson(data);
        setLoading(false);
      });
  }, [personId])

  if (loading === true) {
    return <p>Loading ...</p>
  }

  return <div>
    <p>You're viewing: {person.name}</p>
    <p>Height: {person.height}</p>
    <p>Mass: {person.mass}</p>
  </div>
}

上面代碼中爆班,每當(dāng)組件參數(shù)personId發(fā)生變化衷掷,useEffect()就會執(zhí)行。組件第一次渲染時柿菩,useEffect()也會執(zhí)行


創(chuàng)建自己的 Hooks

上例的 Hooks 代碼還可以封裝起來戚嗅,變成一個自定義的 Hook,便于共享

Person 組件就改用這個新的鉤子,引入封裝的邏輯

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import "./styles.css";

const usePerson = personId => {
  const [loading, setLoading] = useState(true);
  const [person, setPerson] = useState({});
  useEffect(() => {
    setLoading(true);
    fetch(`https://swapi.co/api/people/${personId}/`)
      .then(response => response.json())
      .then(data => {
        setPerson(data);
        setLoading(false);
      });
  }, [personId]);
  return [loading, person];
};

const Person = ({ personId }) => {
  const [loading, person] = usePerson(personId);

  if (loading === true) {
    return <p>Loading ...</p>;
  }

  return (
    <div>
      <p>You're viewing: {person.name}</p>
      <p>Height: {person.height}</p>
      <p>Mass: {person.mass}</p>
    </div>
  );
};

function App() {
  const [show, setShow] = useState("1");

  return (
    <div className="App">
      <Person personId={show} />
      <div>
        Show:
        <button onClick={() => setShow("1")}>Luke</button>
        <button onClick={() => setShow("2")}>C-3PO</button>
      </div>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末渡处,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子祟辟,更是在濱河造成了極大的恐慌医瘫,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旧困,死亡現(xiàn)場離奇詭異醇份,居然都是意外死亡,警方通過查閱死者的電腦和手機吼具,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門僚纷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拗盒,你說我怎么就攤上這事怖竭。” “怎么了陡蝇?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵痊臭,是天一觀的道長。 經(jīng)常有香客問我登夫,道長广匙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任恼策,我火速辦了婚禮鸦致,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涣楷。我一直安慰自己分唾,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布总棵。 她就那樣靜靜地躺著鳍寂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪情龄。 梳的紋絲不亂的頭發(fā)上迄汛,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音骤视,去河邊找鬼鞍爱。 笑死,一個胖子當(dāng)著我的面吹牛专酗,可吹牛的內(nèi)容都是我干的睹逃。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼沉填!你這毒婦竟也來了疗隶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤翼闹,失蹤者是張志新(化名)和其女友劉穎斑鼻,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體猎荠,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡坚弱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了关摇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荒叶。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖输虱,靈堂內(nèi)的尸體忽然破棺而出些楣,到底是詐尸還是另有隱情,我是刑警寧澤悼瓮,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布戈毒,位于F島的核電站,受9級特大地震影響横堡,放射性物質(zhì)發(fā)生泄漏埋市。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一命贴、第九天 我趴在偏房一處隱蔽的房頂上張望道宅。 院中可真熱鬧,春花似錦胸蛛、人聲如沸污茵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泞当。三九已至,卻和暖如春民珍,著一層夾襖步出監(jiān)牢的瞬間襟士,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工嚷量, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留陋桂,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓蝶溶,卻偏偏與公主長得像嗜历,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

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