React Hooks: everything you need to know! ??(譯)

原文地址:React Hooks: everything you need to know! ??

從React 16.8.0開(kāi)始复唤,有新的方法可以優(yōu)雅地調(diào)用異步代碼藐石,從而更輕松地在組件之間重用邏輯亮航。

作為reactjs開(kāi)發(fā)人員萌狂,您有責(zé)任了解最新的react框架功能啄骇。不是為了取悅您的老板,而是要在該領(lǐng)域和市場(chǎng)中保持相關(guān)性灸促。

我仍然記得過(guò)去的美好時(shí)光异旧,當(dāng)時(shí)沒(méi)人在談?wù)搑edux模式,而我的react應(yīng)用程序是狀態(tài)混亂的(2014年中)佣谐。

最初引入flux模式時(shí)肚吏,它很難理解,實(shí)現(xiàn)起來(lái)似乎很復(fù)雜狭魂,但是幾年后罚攀,這是每個(gè)基于React Framework的項(xiàng)目中的標(biāo)準(zhǔn)。

與react hooks將發(fā)生的相同雌澄,是類(lèi)組件的替換和React框架的未來(lái)斋泄。

好的,這將是一篇漫長(zhǎng)的文章镐牺,所以我添加了一個(gè)目錄炫掐,以便您可以閱讀一些內(nèi)容,然后繼續(xù)進(jìn)行項(xiàng)目睬涧,然后在需要休息時(shí)再回來(lái)募胃。

我是唯一閱讀技術(shù)文章以清理思想旗唁,減輕日常工作壓力的人嗎?

內(nèi)容列表

  • 什么是React hooks痹束?
  • React Hook與React Class
  • 現(xiàn)有的React hooks
  • 表示含義
  • useState hook
  • useEffect hook
  • useReducer hook
  • useRef hook
  • 關(guān)注點(diǎn)分離
  • 預(yù)先使用案例
  • 現(xiàn)實(shí)世界中的例子
    • 顯示在線狀態(tài)
    • 跟蹤地理位置
  • 很棒的資源
  • 結(jié)論

什么是React hooks检疫? ??

當(dāng)您使用Reactjs類(lèi)組件時(shí),可以使用狀態(tài)参袱,這就是為什么這些組件也稱(chēng)為有狀態(tài)的原因电谣,而且每個(gè)類(lèi)組件都有生命周期方法秽梅,例如:componentDidMount()抹蚀,componentDidUpdate()等。

您不能在函數(shù)組件中使用任何一種企垦。 函數(shù)組件不能使用自己的狀態(tài)环壤,也沒(méi)有生命周期方法。

現(xiàn)在钞诡,您可以使用React hooks了郑现。

React鉤子使我們能夠使用Reactjs功能組件并為其添加狀態(tài)和生命周期方法。

簡(jiǎn)而言之荧降,React鉤子是特殊函數(shù)接箫,可以擴(kuò)展功能組件的函數(shù),并使其具有生命周期事件和管理狀態(tài)的可能性朵诫。

讓我們比較一下使用React鉤子時(shí)類(lèi)與功能組件的不同之處辛友。

基于類(lèi)的方式良好的舊的流行寫(xiě)法

import React from 'react';
class ClickCounter extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      count: 0 // Initial value for our counter
    };
  }

  setCount(numb) {
    this.setState({
      count: numb
    })
  }

  render() {
    return (
      <div>
        <p>You clicked {count} times</p>
        <button onClick={() => this.setCount(this.state.count + 1).bind(this)}>
          Click me
        </button>
      </div>
    );
  }
}

使用React hooks

import React, { useState } from 'react';
function ClickCounter() {
  /** 
    useState creates a "count" variable that will store the state and a "setCount" function that will mute the "count" variable state.
  **/
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

使用useState hook將狀態(tài)存儲(chǔ)在函數(shù)組件中的示例

更少的代碼行可以完成相同的工作!

不僅如此剪返,借助React鉤子废累,您現(xiàn)在可以重用狀態(tài)邏輯并更好地分離關(guān)注點(diǎn)。

剛開(kāi)始脱盲,這個(gè)新的API可能對(duì)您來(lái)說(shuō)很奇怪邑滨,但繼續(xù)與我一起,您將學(xué)習(xí)如何充分利用它钱反。

現(xiàn)有的React hooks ??

新的API帶有兩個(gè)主要的預(yù)先存在的鉤子掖看,還有一些用于其他用例

基本React hooks

所有React鉤子的基礎(chǔ),您將看到的所有其他鉤子都是這三個(gè)鉤子的變體面哥,或者將它們用作基本體哎壳。

  • useState是狀態(tài)鉤子,用于在組件中聲明狀態(tài)
  • useEffect是副作用掛鉤幢竹,用于將其用于數(shù)據(jù)提取耳峦,手動(dòng)更改DOM等。
  • useContext與Reactjs Context API結(jié)合使用焕毫。 當(dāng)React Context提供程序更新時(shí)蹲坷,此掛鉤將觸發(fā)具有最新上下文值的渲染驶乾。

先進(jìn)的React鉤子

這些是庫(kù)附帶的其他內(nèi)置React鉤子中最重要的。

useReducer是useState的替代方法循签,如果您具有復(fù)雜的狀態(tài)邏輯级乐,則應(yīng)該使用它,如果您對(duì)Redux熟悉的話县匠,會(huì)喜歡它风科。
useRef使用它來(lái)訪問(wèn)帶有可變r(jià)ef對(duì)象的DOM元素。 比ref屬性更有用

那些特殊的括號(hào)

您可能會(huì)問(wèn) const [age乞旦,setAge] = useState(24)的語(yǔ)法含義贼穆,但這只是解構(gòu)數(shù)組的新方法,下面讓我向您展示另一種方法兰粉。

const ageStateVariable = useState(24); // Returns a tuple or an array of length 2
const age = ageStateVariable[0]; // First item
const setAge = ageStateVariable[1]; // Second item

// ES6 way to do this
const [age, setAge] = useState(24);

我喜歡簡(jiǎn)單而優(yōu)雅的單行代碼故痊,不像使用python的人那么多,而且我絕對(duì)不喜歡像使用python的人一樣瘋狂的單行代碼

規(guī)則

  • 切勿從循環(huán)玖姑,條件或嵌套函數(shù)內(nèi)部調(diào)用掛鉤
  • 切勿從常規(guī)函數(shù)調(diào)用掛鉤
  • 僅在函數(shù)組件或自定義hooks中調(diào)用它們
  • Hooks應(yīng)位于組件的頂層
  • Hooks可以調(diào)用其他Hooks

useState hook ??

最容易使用和理解所有的鉤子愕秫。 其目的是將狀態(tài)存儲(chǔ)在函數(shù)組件中。

嗯焰络,從技術(shù)上講戴甩,我們不是將狀態(tài)存儲(chǔ)在其中,而是將其連接到由底層React庫(kù)處理的狀態(tài)的字典(鍵值)中闪彼。 但是我們暫時(shí)不會(huì)深入了解這些細(xì)節(jié)

import React, { useState } from 'react';

function myAwesomeComponent () {
  const [name, setName] = useState('John');
  ...
}

useState返回具有狀態(tài)持有者屬性和setter方法的元組甜孤。

您使用狀態(tài)的初始值調(diào)用useState。

要更新?tīng)顟B(tài)备蚓,請(qǐng)調(diào)用setName函數(shù)

useEffect hook ??

在React類(lèi)中课蔬,通常會(huì)在componentDidMount中設(shè)置一個(gè)訂閱,并在componentWillUnmount中對(duì)其進(jìn)行清理郊尝。

通過(guò)react hook useEffect二跋,我們通過(guò)返回一個(gè)清除或取消訂閱效果的函數(shù)來(lái)執(zhí)行此操作。

如果您使用過(guò)mobx流昏,這種模式可能會(huì)讓您感到熟悉扎即,這類(lèi)似于反應(yīng)。

  useEffect(() => {
    PlacesAPI.subscribeToPlaceNews(props.place.id, handlePlacesNews);
    return () => {
      PlacesAPI.unsubscribeFromPlaceNews(props.place.id, handlePlacesNews);
    };
  });

為什么我們從effect中返回一個(gè)函數(shù)况凉?

這是用于effect的可選擇的清理機(jī)制谚鄙。 每個(gè)effect都可能返回一個(gè)函數(shù),在之后執(zhí)行清除操作刁绒。

這使我們可以保持彼此之間添加和刪除訂閱的邏輯闷营。

useReducer hook ??

當(dāng)您具有復(fù)雜的狀態(tài)邏輯時(shí),最好使用reducer。 如果您熟悉Redux之類(lèi)的庫(kù)或flux模式傻盟,那么您將一眼就理解了速蕊。

Redux pattern architecture

基本上,在您使用reducer調(diào)度或觸發(fā)視圖中的某些操作的情況下娘赴,這些事件將由reducer監(jiān)聽(tīng)规哲,這些reducer具有內(nèi)部邏輯來(lái)更新?tīng)顟B(tài)所在的商店。 現(xiàn)在诽表,當(dāng)商店更新時(shí)唉锌,您的組件將重新渲染。

import React, { useReducer, useState } from 'react';
import produce from 'immer';

function reducer(state, action) {
  switch (action.type) {
    case 'toggle':
      return produce(state, (draftState) => {
        draftState[action.payload].isCompleted = !draftState[action.payload].isCompleted;
      });
    case 'add':
      return produce(state, (draftState) => {
        draftState.push({ label: action.payload });
      });
    default:
      return state;
  }
}

function Todo({ isCompleted, label, onChange }) {
  return <p>
    <label style={{
      textDecoration: isCompleted && 'line-through'
    }}>
      <input
        type="checkbox"
        checked={isCompleted || false}
        onChange={onChange}
      />
      <span>{label}</span>
    </label>
  </p>
}

function TodoList() {
  const todos = [
    { label: 'Do something' },
    { label: 'Buy dinner' }
  ];

  const [state, dispatch] = useReducer(reducer, todos);
  const [newTodo, setNewTodo] = useState('');

  return <>
    {state.map((todo, i) => (
      <Todo
        key={i}
        {...todo}
        onChange={() => dispatch({ type: 'toggle', payload: i })}
      />
    ))}
    <input
      type="text"
      value={newTodo}
      onChange={(e) => setNewTodo(e.target.value)}
    />
    <button onClick={() => {
      dispatch({ type: 'add', payload: newTodo });
      setNewTodo('');
    }}>
      Add
    </button>
  </>;
}

export default TodoList;

useRef hook ??

Refs用于訪問(wèn)render函數(shù)中渲染后的React元素或DOM元素竿奏。 useRef hook返回一個(gè)可變的ref對(duì)象袄简,該對(duì)象的.current屬性已初始化為傳遞的參數(shù)initialValue。 使用非常簡(jiǎn)單

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

關(guān)注點(diǎn)分離 ??

Mantain your code organized

使用Hooks议双,您可以從組件中提取狀態(tài)邏輯痘番,以便可以對(duì)其進(jìn)行獨(dú)立測(cè)試和重用。

Hooks允許您重用狀態(tài)邏輯平痰,而無(wú)需更改組件層次結(jié)構(gòu)。

例如伍纫,組件可能在componentDidMount和componentDidUpdate中執(zhí)行某些數(shù)據(jù)獲取宗雇。

但是,同一componentDidMount方法也可能包含設(shè)置事件偵聽(tīng)器的無(wú)關(guān)邏輯莹规,并在componentWillUnmount中執(zhí)行清理赔蒲。

在一起變化的相互關(guān)聯(lián)的代碼被分開(kāi),但是完全不相關(guān)的代碼最終以單個(gè)方法組合在一起良漱。

  import React from 'react';
  import PlacesAPI from '../services/place';
  class PlaceNewsWithCounter extends React.Component {
    constructor(props) {
      super(props);
      this.handlePlacesNews = this.handlePlacesNews.bind(this);
      this.state = { count: 0, currentEvent: null };
    }

    // Unrelated stateful logic
    componentDidMount() {
      document.title = `You clicked ${this.state.count} times`;
      PlacesAPI.subscribeToPlaceNews(
        this.props.place.id,
        this.handlePlacesNews
      );
    }

    componentDidUpdate() {
      document.title = `You clicked ${this.state.count} times`;
    }

    componentWillUnmount() {
      PlacesAPI.unsubscribeFromPlaceNews(
        this.props.place.id,
        this.handlePlacesNews
      );
    }

    handlePlacesNews(place) {
      this.setState({
        currentEvent: place.currentEvent
      });
    }
    ...
  }

使用React鉤子的更好方法

import React, { useState, useEffect } from 'react';
import PlacesAPI from '../services/place';
function PlaceNewsWithCounter() {

  // Logic for counter here...
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });


  // Logic for place API here...
  const [currentEvent, setCurrentEvent] = useState(null);

  function handlePlacesNews(place) {
    setCurrentEvent(place.currentEvent);
  }

  useEffect(() => {
    PlacesAPI.subscribeToPlaceNews(props.place.id, handlePlacesNews);

    return () => {
      PlacesAPI.unsubscribeFromPlaceNews(props.place.id, handlePlacesNews);
    };
  });


  return ...;
}

提前使用示例

[圖片上傳失敗...(image-353ab6-1602425962902)]

使用useEffect進(jìn)行數(shù)據(jù)提取

通過(guò)結(jié)合使用useEffect和useState舞虱,可以使用useEffect進(jìn)行API調(diào)用,并將空數(shù)組或?qū)ο笞鳛榈诙€(gè)參數(shù)傳遞母市,使其具有與componentDidMount相同的行為矾兜。

這里的關(guān)鍵是第二個(gè)參數(shù)。 如果您不提供空數(shù)組或?qū)ο笞鳛榈诙€(gè)參數(shù)患久,則將在每個(gè)渲染器上調(diào)用API調(diào)用椅寺,并且該調(diào)用實(shí)際上與componentDidUpdate相同

  const [todo, setTodo] = useState(null);
  const [id, setId] = useState(1);
  
  useEffect(() => {
    if (!id) {
      return;
    }
    
    fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
      .then(results => results.json())
      .then(data => {
        setTodo(data);
      });
  }, [id]);  // Don't forget to add this!

通過(guò)將第二個(gè)參數(shù)傳遞給useEffect,我們將在id屬性更改時(shí)設(shè)置訂閱蒋失,從而重新觸發(fā)效果

如果相反返帕,我們只想在該組件掛載時(shí)進(jìn)行API調(diào)用

const [fullName, setFullName] = useState(null);

useEffect(() => {
  fetch('https://randomuser.me/api/')
    .then(results => results.json())
    .then(data => {
      const {name} = data.results[0];
      setFullName(`${name.first} ${name.last}`);
    });
}, []); // <-- Have to pass in [] here!
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市篙挽,隨后出現(xiàn)的幾起案子荆萤,更是在濱河造成了極大的恐慌,老刑警劉巖铣卡,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件链韭,死亡現(xiàn)場(chǎng)離奇詭異邑闲,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)梧油,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)苫耸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人儡陨,你說(shuō)我怎么就攤上這事褪子。” “怎么了骗村?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵嫌褪,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我胚股,道長(zhǎng)笼痛,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任琅拌,我火速辦了婚禮缨伊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘进宝。我一直安慰自己刻坊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布党晋。 她就那樣靜靜地躺著谭胚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪未玻。 梳的紋絲不亂的頭發(fā)上灾而,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音扳剿,去河邊找鬼旁趟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛舞终,可吹牛的內(nèi)容都是我干的轻庆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼敛劝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼余爆!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起夸盟,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蛾方,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體桩砰,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拓春,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亚隅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片硼莽。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖煮纵,靈堂內(nèi)的尸體忽然破棺而出懂鸵,到底是詐尸還是另有隱情,我是刑警寧澤行疏,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布匆光,位于F島的核電站,受9級(jí)特大地震影響酿联,放射性物質(zhì)發(fā)生泄漏终息。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一贞让、第九天 我趴在偏房一處隱蔽的房頂上張望周崭。 院中可真熱鬧,春花似錦震桶、人聲如沸休傍。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至人柿,卻和暖如春柴墩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凫岖。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工江咳, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人哥放。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓歼指,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親甥雕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子踩身,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

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