自從React提出Hook這個概念到如今已經有一年多的時間了,在Reactv16.8.0版本中正式發佈,到目前爲止,Hook的特性已經比較穩定,同時Hook的出現也解決了咱們長期使用React所帶來的的一些痛點,最近在項目中初步接觸到React Hooks,因此藉着這個時間來細緻的學習一下React Hooks。react
你還在爲該使用無狀態組件(Function)仍是有狀態組件(Class)而煩惱嗎? ——擁有了hooks,你不再須要寫Class了,你的全部組件都將是Function。設計模式
你還在爲搞不清使用哪一個生命週期鉤子函數而日夜難眠嗎? ——擁有了Hooks,生命週期鉤子函數能夠先丟一邊了。數組
你在還在爲組件中的this指向而暈頭轉向嗎? ——既然Class都丟掉了,哪裏還有this?你的人生第一次再也不須要面對this。bash
咱們常寫的React組件通常分爲兩種,有狀態組件和無狀態組件,即Class組件和函數組件,函數組件相對於Class組件來講,主要具備這些特性:app
從這些點來看,函數組件無疑是更好的選擇,沒有任何反作用,組件是可預測的,但在實際開發中,咱們由於複雜的業務邏輯而不得不選擇Class組件,而且隨着項目的不斷維護更改,整個組件內部的邏輯也會愈來愈複雜,代碼愈來愈臃腫,因此這個時候的React Hooks出現了,Hook 是 React 16.8 的新增特性。它可讓你在不編寫 class 的狀況下使用 state 以及其餘的 React 特性。,這是官方對Hook的解釋,按照我本身的理解,它的出現,使得咱們可以在函數組件的基礎上,維護一個state,而且可以代替咱們須要在生命週期中所完成的一些操做。dom
首先來看一下官方示例ide
import React, { useState } from 'react';
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>
);
}
}
複製代碼
import React, { useState } from 'react';
function Example() {
// 聲明一個叫 "count" 的 state 變量
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
複製代碼
整段代碼是否是簡潔了不少!組件從Class組件變成函數組件,而且在使用useState這個hook之後,擁有了本身的狀態,還能夠經過setCount隨時改變改變本身的狀態。固然,React Hooks除了useState這個hook之外,還有包括useEffect(提供相似生命週期函數的做用), useContext(提供上下文的功能)等一系列其餘hook,而且還能夠自定義hook來知足咱們不一樣的需求。函數
React在組件層面作到了高內聚,低耦合,組件能夠說是react的一個核心,一個頁面由大大小小不一樣的許多個組件構成,可是在一個實際項目中,一個組件的代碼實際上是很長的,加上組件自己的state和聲明周期函數中的一些操做,使得咱們真正去複用一個組件變的不是很簡單,以前官方推薦的解決方法是渲染屬性(Render Pros)和高階組件(HOC)學習
渲染屬性指的是使用一個值爲函數的 prop 在 React 組件之間的代碼共享。說白了就是經過傳遞props給一個組件之後返回一個組件給咱們,就像這樣 :ui
<DataProvider>
<Component data={data} />
</DataProvider>
複製代碼
至於高階組件其實就是一個函數,一個以組件爲參數,而且返回一個新的組件的函數。說白了就是一個函數對傳入的組件進行了處理之後在返回,就像這樣
function logProps(InputComponent) {
InputComponent.prototype.componentWillReceiveProps = function(nextProps) {
console.log('Current props: ', this.props);
console.log('Next props: ', nextProps);
};
// 返回原始的 input 組件,暗示它已經被修改。
return InputComponent;
}
// 每次調用 logProps 時,加強組件都會有 log 輸出。
const EnhancedComponent = logProps(InputComponent);
複製代碼
這兩種模式乍看起來確實以爲不錯,做爲一個小白,說實話我一直寫的也挺爽的,可是使用這兩種設計模式,會不可避免的增長咱們的代碼層級,致使組件嵌套過深 ,隨便打開一個網頁都能看到這種嵌套很深的dom結構,因此隨着今天的深刻,也發現了這兩種模式並不是最優解。
寫了也有接近半年的React了,可是直到如今,除了最經常使用的幾個生命週期函數之外,每次用到其餘的生命週期函數,我都要去查一下生命週期表單來確認有沒有寫錯,更不用說複雜組件中生命週期函數的使用了,咱們須要在生命週期中實現獲取數據,監聽事件,取消訂閱等等一系列的邏輯,隨着組件的不斷維護擴充,組件也就變得愈來愈難以理解
class中最難以理解的應該是this了吧,爲了保證this的指向符合預期,this.funtion = this.funtion.bind(this)寫了無數遍, <div onClick={(data) => this.function(data)}> </div>
也常常會寫,數不清的this寫起來確實很麻煩
爲了消滅這些開發過程當中的問題,Hooks來了!(他來了他來了,他踩着祥雲走來了!)
export default function CountDown(props) {
const tmr = useRef(0);
const [stop, setStop] = useState(false);
const [stopTime, setStopTime] = useState(0);
const [refresh, setRefresh] = useState(props.refresh);
const handleRefresh = useCallback(() => {
clearTimeout(tmr.current);
tmr.current = setTimeout(() => {
setRefresh(refresh - 1);
if (refresh == 0) {
const { onFinish } = props;
onFinish && onFinish();
setRefresh(props.refresh);
}
}, 1000);
}, [setRefresh, refresh, props]);
const handleClick = useCallback(() => {
if (!stop && refresh > 0) {
clearTimeout(tmr.current);
setStop(true);
setStopTime(refresh);
} else if (stop) {
setStop(false);
// setRefresh(stopTime);
handleRefresh();
}
}, [stop, refresh, stopTime, setRefresh]);
useEffect(() => {
handleRefresh();
}, [refresh]);
return (
<div className={styles.wrapper}>
{
stop ? (
<div onClick={handleClick} >
{
stopTime < 10 ? '0' + stopTime : stopTime
}
</div>
) : (
<div onClick={handleClick}>
{
refresh < 10 ? '0' + refresh : refresh
}
</div>
)
}
</div>
);
}
複製代碼
這是一個倒計時組件,當倒計時結束時會調用回調函數,執行而後從新計時,讓咱們一塊兒看一下它是怎麼實現的吧: 首先,聲明咱們須要維護的變量stop、stopTime、refresh,聲明tmr時注意,若是咱們要聲明一個普通變量,最好使用useRef來聲明,聲明之後初始值會存儲在tmr.current中,之後每次修改也都是修改tmr.current,這樣作的好處是可以確保tmr不會丟失,由於整個函數每次都會徹底從新渲染,因此很容易形成咱們最新渲染出來的變量沒有指向先前的修改從而致使bug的產生, 接下來,使用useCallback來聲明兩個咱們須要用到的函數分別用來處理點擊事件和回調事件。useCallback(() => {},[]), []中必定要將咱們函數中須要用到的依賴也就是用到的變量聲明,這裏的目的也是爲了數據的指向精準,最後聲明useEffect(() => {},[]) []這裏的數組裏面須要寫什麼呢? 你但願當哪一個變量發生變化時執行這個useEffect呢? 但願哪一個就寫入哪一個, 若是寫入一個空數組,那麼做用就至關於componentDidMount只會執行一次,若是像這樣什麼也沒有寫,useEffect(() => {}),那麼每次組件從新渲染時,這個useEffect都會執行
總結,React Hooks的出現,極大的解決了咱們目前在用react實現複雜項目時的一些痛點,可是裏面的坑也比較多,這篇文章只是初步科普一下React Hooks的使用,至於更深一步好比爲何會有精準依賴,Hooks的數據是怎麼保持的等等要抽出更多的時間來了解Hooks的源碼實現,未完待續~