React 16新特性全解 (下)--- 一文讀懂Hooks

前言

這篇文章主要講Hooks,若是你想了解React 16的其餘新特性,請移步 React 16新特性全解 (上)React 16新特性全解 (中)html

v16.8 Hooks

Hooks是什麼?react

咱們知道,functional component在使用的時候有一些限制,好比須要生命週期、state的時候就不能用functional component。 而有了Hooks,你就能夠在funtional component裏,使用class component的功能:props,state,context,refs,和生命週期函數等等。npm

雖然Hooks已經有要取代正宮class的趨勢了,可是react目前沒有計劃拋棄class,因此不要慌,你仍是能夠跟往常同樣使用class。bash

在真正介紹Hook以前,仍是同樣先來了解爲何要引入Hooks?其實不僅僅是爲了給functional component賦於class component的功能。app

還有下面的問題:函數

1.組件之間很難複用邏輯工具

以前若是咱們須要複用邏輯,經常使用的兩種方式是render propshigher-order components。可是這兩種方式都須要你重構代碼,因此比較麻煩。學習

最重要的是,用這兩種方式的話,在React Devtools裏,會看到不少的嵌套組件。fetch

在這個圖能夠看到Header外層套着不少嵌套組件。ui

2.複雜組件很難理解

在以前的class component裏,咱們的生命週期函數裏一般放着不相關的代碼,而相關的代碼確要放在不一樣的生命週期函數裏。這樣說可能有點繞,咱們來看一個具體的例子。

class App extends React.component {
  componentDidMount() {
    window.addEventListener('scroll', () => {console.log('a')})
    this.fetchData();
  }
  componentDidUpdate() {
    this.fetchData();
  }
  componentWillUnmount() {
    window.removeEventListener('scroll', () => {console.log('a')})
  }
  render() {
    return <div>ddd</div>
  }
}
複製代碼

這應該是咱們平時會常常寫的代碼。在componentDidMount裏作事件綁定,獲取數據。在componentWillUnMount裏解綁事件。

這樣作的問題是:componentDidMount裝着的代碼都是不相關的,而相關聯的事件綁定以及事件解綁,分散在componentDidMount 跟 componentWillUnMount裏。這樣若是組件的邏輯越寫越複雜以後,就會變得很難維護易出bug。

後面講Effect Hook的時候,我會介紹這個問題用Hooks怎麼解決。

3.class比較難學

React團隊發現class是初學者學習React的大障礙。要學習class component,你必需要知道幾點:

  1. this在JS是如何工做的(光是這個就夠繞的)
  2. 記得綁定事件
  3. 瞭解state,props,state以及從上而下的數據流
  4. functional component跟class component的區別,如何使用它們

如何使用

理解了Hooks誕生的緣由,接着來看看要如何使用。

假設我須要實現一個功能,點擊app時候,count數目加一。用class的形式實現就是 這樣的:

Class:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}
複製代碼

Hooks:

若是須要用Hooks實現,變成

import React, { useState } from 'react';
    
    function Example() {
      // Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } 複製代碼

演示地址

在這裏demo中。useState就是Hook。咱們經過它來在function component里加入state數據。

二者對好比下:

1. 定義state變量

Class:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
複製代碼

Hooks:

import React, { useState } from 'react';

function Example() {
  // 經過useState這個Hooks定義state變量:count。而且經過useState給count賦初始值0,只在初始化時候使用一次
  const [count, setCount] = useState(0);
複製代碼

在function component裏,咱們是沒有this的。因此沒辦法向Class那樣用this來建立state,這時候Hooks閃亮登場。 經過useState這個hooks咱們能夠定義count這個state變量。 由Hooks定義的state變量不必定要是object,能夠是string、number。傳入的內容至關於給變量賦初始值。

function ExampleWithManyStates() {
  // Declare multiple state variables!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}

複製代碼

2. 渲染state

Class:

<p>You clicked {this.state.count} times</p>
複製代碼

Hooks:

<p>You clicked {count} times</p>
複製代碼

能夠不須要用this,直接使用count

3.更新state

Class:

<button onClick={() => this.setState({ count: this.state.count + 1 })}>
    Click me
  </button>
複製代碼

Hooks:

<button onClick={() => setCount(count + 1)}>
    Click me
  </button>
複製代碼

咱們能夠看到 const [count, setCount] = useState(0);useState返回兩個參數,一個是當前state的值,還有一個實際上是一個函數,用來改變state的值,就是setCount。

相似setState,可是不一樣的是,它不會將舊的state跟新的state合併在一塊兒,而是覆蓋式的重寫state的值。

說完了functional component裏面如何使用state以後,咱們再來看如何用Effect Hook來取代生命週期。

通常咱們都會在生命週期componentDidMount, componentDidUpdate與 componentWillUnmount中作一些反作用的操做,例如:獲取數據,訂閱,手動改變DOM。而在hooks裏,這些生命週期函數都被統一成一個方法 useEffect。

下面咱們來舉個例子:

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

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

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

在這個例子中,咱們在useEffect 裏完成了反作用的操做。demo演示

Effects Hooks 就在functional component裏, 因此它能夠直接訪問props跟state。 React在每次render以後都會調用effect,包括第一次render。

可是這裏還遺留兩個問題

一、咱們在開篇說到,class component有個問題就是生命週期函數裏的代碼都是不相關的,而相關的代碼確要被打散在不一樣的生命週期函數裏。這個問題用Hooks的話就能夠解決。好比綁定、解綁事件,在使用class的時候,在componentDidMount裏 監聽了一個事件,以後須要在componentWillMount裏給它解綁。

用Hook只須要在useEffect一個函數就能夠作到。它能夠經過返回一個函數來專門作清除的工做,代碼以下:

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

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
   
    // 在這裏返回一個函數來作這件事
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
複製代碼

在這個case中,unsubscribeFromFriendStatus不只僅會在組件unmount的時候 調用,同時在從新渲染的時候也會調用。

若是你只想useEffect只在mount與unmount時候調用,須要這樣傳一個[] 做爲第二個參數。

useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);

    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  }, []);

複製代碼

二、有時候咱們並不想每次state的改變,都去調用useEffect。 在class裏,會這樣作

componentDidUpdate(prevProps, prevState) {
  if (prevState.count !== this.state.count) {
    document.title = `You clicked ${this.state.count} times`;
  }
}
複製代碼

在Hooks,能夠經過給useEffect傳入第二個參數,即它關注的state變量,來作到這件事。

例如:當count改變時候,纔去調用useEffect。

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
複製代碼

注意事項:

  1. Hooks只能夠在頂層使用。也就是說它不能寫在循環體,條件渲染,或者嵌套function裏
  2. 只能夠在React的function組件裏使用Hooks。

說了那麼多,總結如下,其實Hooks就是幫助咱們在function component裏直接使用原來class component纔有的特性。

以上,咱們只接觸到了兩種hooks,還有更多好比useContext, useReducer, useCallback,感興趣的同窗能夠本身看下~

最後是使用Hooks的一些建議:

一些建議

  1. 是否須要用hoooks重構之前的代碼?

No,react團隊不推薦用hooks從新寫一遍。 推薦作法是新的組件能夠直接使用,而後須要改老組件代碼的時候在順便改就好了。s

  1. 同時建議使用

Hooks可讓你使用在不須要寫class的狀況,只用

  1. 支持Hooks的工具

React DevTools對hooks已經支持。同時強烈推薦安裝hooks的eslint校驗庫 eslint-plugin-react-hooks

Reac團隊下一步計劃

由於如今React Hooks還不能徹底cover全部class的功能,雖然他們已經很相近了。目前爲止Hooks不支持兩個不常使用的API getSnapshotBeforeUpdate 跟 componentDidCatch,可是React團隊正在努力,將來會支持的。

相關文章
相關標籤/搜索