React hooks已經出來有一段時間了,但是許多React開發人員對它們的態度並不是很積極。我發現這背後主要有兩個原因。第一個原因是許多React開發人員已經參與了一個大型項目,需要付出巨大的努力才能遷移整個代碼庫。另一個原因是大家對React類已經很熟悉了。有足夠經驗的話,繼續使用類會感到更自在。
在本文中,我們將探討考慮使用React Hooks的六個原因。
1. 擴展函數式組件時,不必將其重構爲類組件
經常會有這種情況,那就是一個React組件從一個函數式組件開始開發,一開始這個函數式組件只依賴props,後來演變爲具有狀態的類組件。從函數式組件更改爲類組件需要一些重構工作,具體取決於組件的複雜程度。
使用React Hooks時,由於函數式組件具有進入狀態的能力,因此重構工作會非常少。來看以下示例,這是一個啞組件,它會顯示一個帶有計數的標籤。
export function ShowCount(props) {
return (
<div>
<h1> Count : {props.count} </h1>
</div>
);
}
假設我們需要通過點擊鼠標來增加計數,並假設這隻會影響這一個組件。第一步,我們需要將狀態引入組件。我們看一下如何使用基於類的方法。
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>
);
}
}
如果使用hooks,則相同的組件會是下面這樣。
export function ShowCount(props) {
const [count, setCount] = useState();
useEffect(() => {
setCount(props.count);
}, [props.count]);
return (
<div>
<h1> Count : {count} </h1>
</div>
);
}
2. 你不必再擔心“this”了
人類和機器都會因爲類而困惑
上面這句話來自React文檔。造成這種混亂的原因之一是this關鍵字。如果你熟悉JavaScript,就會知道JavaScript中的this與其他語言並不完全一樣。但在React Hooks這邊,你完全不必操心this了。這對初學者和經驗豐富的開發人員來說都是有益的。
根據上面的示例,你可以看到訪問狀態時我們不再需要使用“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>
);
}
}
我們引入了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>
);
}
如你所見,我們只添加了這個函數。另外你可能會注意到,當我們使用事件處理程序時,已經在模板中刪掉了this。
onClick={ handleClickEvent }
4. 更容易分離邏輯與UI,從而使兩者都更加可重用
使用hooks時,邏輯和UI更容易分離。無需HOC或渲染props。hooks用更少的樣板和更直觀的UI和邏輯組合來優雅地做到這一點。
當使用Bit之類的工具和平臺共享組件時,這種“優雅的分離”尤其重要,因爲每個(獨立共享的)組件在不同應用程序之間都會更容易理解、維護和重用。
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;
}
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>
);
}
你可以看到在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>
);
}
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,我們可以像下面這樣重寫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組件中調用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