使用React Hooks代替類的6大理由

React hooks已經出來有一段時間了,但是許多React開發人員對它們的態度並不是很積極。我發現這背後主要有兩個原因。第一個原因是許多React開發人員已經參與了一個大型項目,需要付出巨大的努力才能遷移整個代碼庫。另一個原因是大家對React類已經很熟悉了。有足夠經驗的話,繼續使用類會感到更自在。

在本文中,我們將探討考慮使用React Hooks的六個原因。

1. 擴展函數式組件時,不必將其重構爲類組件

經常會有這種情況,那就是一個React組件從一個函數式組件開始開發,一開始這個函數式組件只依賴props,後來演變爲具有狀態的類組件。從函數式組件更改爲類組件需要一些重構工作,具體取決於組件的複雜程度。

使用React Hooks時,由於函數式組件具有進入狀態的能力,因此重構工作會非常少。來看以下示例,這是一個啞組件,它會顯示一個帶有計數的標籤。

export function ShowCount(props) {
  return (
    <div> 
      <h1> Count : {props.count} </h1>
    </div>
  );
}
ShowCount函數式組件

假設我們需要通過點擊鼠標來增加計數,並假設這隻會影響這一個組件。第一步,我們需要將狀態引入組件。我們看一下如何使用基於類的方法。

export class ShowCount extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
  componentDidMount() {
    this.setState({
      count: this.props.count
    })
  }
  render() {
    return (
      <div> 
        <h1> Count : {this.state.count} </h1>
      </div>
    );
  }
  
}
引入狀態後的ShowCount組件

如果使用hooks,則相同的組件會是下面這樣。

export function ShowCount(props) {
  const [count, setCount] = useState();
  useEffect(() => {
    setCount(props.count);
  }, [props.count]);
  return (
    <div>
      <h1> Count : {count} </h1>
    </div>
  );
}
帶hooks的ShowCount組件

2. 你不必再擔心“this”了

人類和機器都會因爲類而困惑

上面這句話來自React文檔。造成這種混亂的原因之一是this關鍵字。如果你熟悉JavaScript,就會知道JavaScript中的this與其他語言並不完全一樣。但在React Hooks這邊,你完全不必操心this了。這對初學者和經驗豐富的開發人員來說都是有益的。

分別使用hooks與類訪問狀態的對比

根據上面的示例,你可以看到訪問狀態時我們不再需要使用“this”。這樣大家就都不會感到困惑了。

3. 不再有方法綁定

現在,對於前面提到的那個ShowCount組件,我們將引入一種方法,當用戶單擊標籤時,該方法可以更新狀態計數。

export class ShowCount extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
    this.handleClickEvent = this.handleClickEvent.bind(this);
  }
  componentDidMount() {
    this.setState({
      count: this.props.count
    });
  }
  handleClickEvent() {
    this.setState({count: this.state.count + 1});
  }
  render() {
    return (
      <div>
        <h1 onClick={this.handleClickEvent} > Count : {this.state.count} </h1>
      </div>
    );
  }
}
具有Click事件處理程序的ShowCount組件

我們引入了handleClickEvent方法。要使用它,首先我們必須將其綁定到該組件的this上。

this.handleClickEvent = this.handleClickEvent.bind(this);

我們必須這樣做,因爲執行方法時的執行上下文是不一樣的。對於初學者來說,這可能有點難以理解。

除了綁定所有方法之外,還有一些語法提案可以解決這個問題。例如,我們可以將這個函數重寫爲一個箭頭函數。

handleClickEvent = () => {
  this.setState({count: this.state.count + 1});
}

讓我們看看如何使用hooks實現相同的功能。

export function ShowCount(props) {
  const [count, setCount] = useState();
  useEffect(() => {
    setCount(props.count);
  }, [props.count]);
  function handleClickEvent() {
    setCount(count + 1);
  }
  return (
    <div>
      <h1 onClick={handleClickEvent}> Count : {count} </h1>
    </div>
  );
}
hooks中帶有事件處理程序的ShowCount組件

如你所見,我們只添加了這個函數。另外你可能會注意到,當我們使用事件處理程序時,已經在模板中刪掉了this。

onClick={ handleClickEvent }

4. 更容易分離邏輯與UI,從而使兩者都更加可重用

使用hooks時,邏輯和UI更容易分離。無需HOC或渲染props。hooks用更少的樣板和更直觀的UI和邏輯組合來優雅地做到這一點。

當使用Bit之類的工具和平臺共享組件時,這種“優雅的分離”尤其重要,因爲每個(獨立共享的)組件在不同應用程序之間都會更容易理解、維護和重用。

Bit.dev上的共享React組件

5.將相關邏輯放在同一位置

複雜的組件會變得難以理解

使用基於類的方法,我們會有不同的生命週期方法,例如componentDidMount和componentDidUpdate等。讓我們考慮一種情況,就是在componentDidMount中訂閱服務A和B,然後在componentWillUnmount中取消訂閱它們。隨着時間的流逝,兩種生命週期方法中都會包含許多邏輯,並且很難跟蹤掛載與卸載過程中哪些部分是相關聯的。

爲了演示這一點,我們來創建一個基於RxJs的服務來獲取計數。我們將使用這個服務來更新ShowCount示例中的計數。請注意,由於不再需要在click事件中更新組件,我們將刪除handleClickEvent。

import { Subject } from "rxjs";
export function getCounts() {
  const subject = new Subject();
  let count = 0;
  const interval = setInterval(() => {
    if (count > 10 || subject.isStopped) {
      clearInterval(interval);
      subject.complete();
    }
    subject.next(count++);
  }, 1000);
  return subject;
}
getCounts函數
import { getCounts } from "./reactive-service";
export function ShowCount(props) {
  const [count, setCount] = useState();
  useEffect(() => {
    const countServiceSubject = getCounts();
    countServiceSubject.subscribe((count) => {
      setCount(count);
    });
    return () => {
      countServiceSubject.unsubscribe();
    };
  }, []);
  return (
    <div>
      <h1> Count : {count} </h1>
    </div>
  );
}
帶有Effect hook中getCounts方法的ShowCount組件

你可以看到在useEffect內部,我們包括了訂閱以及相應的取消訂閱邏輯。同樣,如果我們需要引入更多的服務訂閱或不相關的邏輯,則可以添加多個useEffect塊,在邏輯上分離不同的關注點。

import { getCounts } from "./reactive-service";
export function ShowCount(props) {
  const [count, setCount] = useState();
  const [secondCount, setSecondCount] = useState(0);
  useEffect(() => {
    const countServiceSubject = getCounts();
    countServiceSubject.subscribe((count) => {
      setCount(count);
    });
    return () => {
      countServiceSubject.unsubscribe();
    };
  }, []);
  useEffect(() => {
    setSecondCount(secondCount + 1);
  }, []);
  return (
    <div>
      <h1> Count : {count} </h1>
      <h1> Second Count: {secondCount} </h1>
    </div>
  );
}
多個useEffect塊可分離不相關的邏輯

6. 在組件之間共享狀態邏輯

使用基於類的方法時,我們很難在組件之間共享狀態邏輯。考慮兩個組件,這兩個組件都必須從兩個不同的數據源獲取、排序和顯示數據。即使兩個組件具有相同的功能,它們之間也很難共享邏輯,因爲這些組件具有不同的源和狀態。

雖然我們可以使用渲染props高階組件來解決這個問題,但由於我們必須重構組件,這會引入額外的成本,到頭來會變得更麻煩。

React Hooks又是怎麼做的呢?

使用自定義React Hooks,你可以提取這些可重用的狀態邏輯並分別測試它們。

我們可以從ShowCount示例中提取一個自定義hook。

import { useState, useEffect } from "react";
export function useCount(serviceSubject) {
  const [count, setCount] = useState();
  useEffect(() => {
    serviceSubject.subscribe((count) => {
      setCount(count);
    });
    return () => {
      serviceSubject.unsubscribe();
    };
  }, [serviceSubject]);
  return [count, setCount];
}
自定義hook來共享狀態邏輯

使用上面的自定義hook,我們可以像下面這樣重寫ShowCount組件。注意,我們必須將數據源作爲參數傳遞給這個自定義hook。

import { useCount } from "./use-count";
export function ShowCount(props) {
  const [count, setCount] = useCount(props.serviceSubject);
  useEffect(() => {
    setCount(-1);
  }, [setCount]);
  return (
    <div>
      <h1> Count : {count} </h1>
    </div>
  );
}
ShowCount組件,帶有數據源參數

請注意,我們在父組件,而不是ShowCount組件中調用getCounts。否則,serviceSubject每次運行ShowCount時都將有一個新值,而我們將無法獲得預期的結果。

結論

切換到React Hooks的理由有很多,但我已經提到了其中一些最令人信服的原因,這些足夠讓我改用React Hooks了。如果查看官方文檔,你會發現React Hooks有很多有趣的功能。請大家也談一談自己的React Hooks之旅吧。

你可以在此處找到完整的源代碼。

原文鏈接:

https://blog.bitsrc.io/6-reasons-to-use-react-hooks-instead-of-classes-7e3ee745fe04

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章