紫棋的一句想要我幫你唱hook嗎?讓hook紅了一把!
作爲一個開發,尤爲是前端開發,在電視上聽到這個詞仍是有點小興奮的(雖然彼hook非此hook)。玩夠這個梗,是否是也要了解一下本身的react hook?html
使用react有一段時間了,組件化編程也早已成爲習慣。經常使用的兩種編寫組件的方式就是就是class組件和函數組件。
class組件:經過繼承React.Component來構建組件,雖然提供了state狀態和完備的生命週期函數,可是也有不少不方便的地方。前端
componentDidMount() { document.title = `You clicked ${this.state.count} times`; ChatAPI.subscribeToFriendStatus( this.props.friend.id, this.handleStatusChange ); } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } componentWillUnmount() { ChatAPI.unsubscribeFromFriendStatus( this.props.friend.id, this.handleStatusChange ); }
函數組件:經過函數直接書寫的組件,看起來是簡潔了許多,可是不能使用state,也沒有生命週期函數、更不能使用react的一些其餘特性。只能經過父組件傳遞進來,任人魚肉,只能慘淡的淪爲展現組件。react
那麼,函數組件就只能淪爲展現的花瓶嗎?能結合class組件能使用react特性的優勢和函數組件簡潔優雅的特性嗎?好在react hook來了,函數組件的春天來了。編程
React 16.8 新增Hook特性。它可讓你在不編寫 class 的狀況下使用 state 以及其餘的 React 特性。api
是否是感受hook挺神奇的?hook是什麼?怎麼使用?不用驚訝,hook就是一個鉤子函數,鉤着react的一些api供給函數組件使用。數組
先看一個官方的useState例子:函數
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> ); }
引入一個hook(useState函數),傳入一個初始值0,而後函數返回了一個數組,經過數組的結構賦值,取得state, count = 0和修改count值的函數setCount。當button發生點擊事件時,觸發setCount函數,而且傳入新的count值完成對state值的修改,並展現到頁面上。代碼轉換成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> ); } }
看明白了嗎?性能
Hook 是一些可讓你在函數組件裏「鉤入」 React state 及生命週期等(context、refs)特性的函數。
React 內置了一些像 useState 這樣的 Hook。你也能夠建立你本身的 Hook 來複用不一樣組件之間的狀態邏輯。優化
一個組件中可使用多個hook,每一個hook會獨立存在,內部狀態不會共用。每次更新組件時,hook會按順序從上到下執行。
function Form() { // 1. Use the name state variable const [name, setName] = useState('Mary'); // 2. Use the age state variable const [age, setAge] = useState('12'); // 3. Use the surname state variable const [surname, setSurname] = useState('Poppins'); // ... } 第一次執行結果: 設置狀態name Mary; 設置狀態age 12; 設置狀態surname Poppins; 第二次執行結果: 設置狀態name Mary; 設置狀態age 12; 設置狀態surname Poppins;
那麼,react是如何知道哪一個 state 對應 哪個useState?很簡單, React 靠的是 Hook 調用的順序。
react hook 使用,要遵循下面兩個規則:
只在最頂層使用 Hook
react依靠hook調用順序來對應state,因此調用順序不能變。
function Form() { // 1. Use the name state variable const [name, setName] = useState('Mary'); // 2. Use the age state variable if(!name){ const [age, setAge] = useState('12'); } // 3. Use the surname state variable const [surname, setSurname] = useState('Poppins'); // ... } 第一次執行結果: 設置狀態name Mary; 設置狀態age 12; 設置狀態surname Poppins; 第二次執行結果: 設置狀態name Mary; 設置狀態age 12;//判斷後會被忽略 設置狀態surname 12;//此時設置爲12,與需求已不符
也就是說只能在頂層使用hook。
在class組件中是沒法使用hook函數的,只能在函數組件中使用。
在自定義hook中也可使用。
上面使用的useState Hook是一種可讓你在函數組件內使用state的內置hook,除此以外react還提供了許多內置hook:
基礎 Hook
useState useEffect useContext
額外的 Hook
useReducer useCallback useMemo useRef useImperativeHandle useLayoutEffect useDebugValue
下面看一下 useState、useEffect兩個經常使用的hook,其餘的使用頻率較低,使用方法也都基本一致其餘hook
咱們想在函數組件里加入state,點擊+,完成數字累加功能:
import React , {useState} from 'react' function Item(){ const [count,setCount] = useState({name:'tom',age:11}) return <> {`${count.name}已經${count.age}歲了`} <div onClick={()=>{setCount({name:'janny',age:count.age+1})}}>+</div> </> } export default Item;
從上面代碼能夠看出來,useState hook提供了在函數組件裏使用state的能力。十分簡潔。
首先,經過react引入useState鉤子函數;
接着,在函數組件內調用useState,並傳入state的初始值,能夠是個值,也能夠是對象。useState函數的返回值個數組,這裏經過數組的結構賦值取得count(變量名能夠隨意定義)和修改count的方法setCount(函數名也能夠隨意定義)。
取值state:能夠經過變量count直接取值
修改state:經過給setCount函數傳遞值來修改
是否是很簡單?之後終於能夠在函數組件裏使用state了,擁有控制本身主權的能力了!除了使用state,還有一種場景也是咱們常常碰到的,那就是函數組件沒有生命週期,一些反作用的操做無法完成!彆着急,react又內置了極好的effect hook!
當咱們點擊+時,不只想完成數字累加,並且想要在title裏顯示咱們的修改,若是在class組件裏,咱們能夠在componentDidMount生命週期裏經過setState完成,可是函數組件裏無法完成,即便如今有useState Hook也沒有生命週期函數,無法完成!那就在來一個hook吧,引入effect Hook。
useEffect hook不只提供了生命週期,並且useEffect Hook能夠看作componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數的組合。也就是在這一個鉤子函數裏能夠完成不少事!
import React , {useState,useEffect} from 'react' function Item(){ const [count,setCount] = useState({name:'tom',age:11}) useEffect(()=>{ document.title = count.age }) return <> {`${count.name}已經${count.age}歲了`} <div onClick={()=>{setCount({name:'tom',age:count.age+1})}}>+</div> </> } export default Item;
好好感覺一下!useEffect就像是一個生命週期函數。這個功能在 class 組件中,須要在兩個生命週期函數(componentDidMount、componentDidUpdate)中編寫重複的代碼。在函數useEffect內進行反作用操做就好了。由於每次掛載、更新後useEffect都會執行。
在 React 組件中有兩種常見反作用操做:須要清除的和不須要清除的。上面這種就是不須要清除的反作用。
假如,咱們以爲只用手指點擊太累了,想弄個計時器幫咱們執行。
let timer = null; function Item(){ const [count,setCount] = useState({name:'tom',age:11}) useEffect(()=>{ clearInterval(timer);//清除 timer = setInterval(()=>{ console.log(1) setCount({name:'tom',age:count.age+1}) },1000) document.title = count.age }) return <> {`${count.name}已經${count.age}歲了`} <div onClick={()=>{setCount({name:'tom',age:count.age+1})}}>+</div> </> }
這樣就很棒,換上自動擋就很舒服!可是當咱們切換路由,移除這個組件後,出去看看風景回來!發現定時器沒有停,本身玩的很嗨...哎呦喂!
這就是須要清除的反作用,class組件裏咱們能夠經過在componentWillUnmount生命週期函數裏清除,因此這裏咱們也要清除這些須要清除的反作用!
function Item(){ const [count,setCount] = useState({name:'tom',age:11}) useEffect(()=>{ clearInterval(timer) timer = setInterval(()=>{ console.log(1) setCount({name:'tom',age:count.age+1}) },1000) document.title = count.age; return function(){//add clearInterval(timer) } }) return <> {`${count.name}已經${count.age}歲了`} <div onClick={()=>{setCount({name:'tom',age:count.age+1})}}>+</div> </> }
useEffect清除反作用的方式也很簡單,return 一個函數,在函數裏清除就能夠了。
上面說了useEffect hook在每次掛載、更新後都會執行。這不就是跟setState在性能方面形成的問題同樣嚴重嗎?class組件 經過在 componentDidUpdate 進行優化,useEffect怎麼優化!
能夠經過useEffect函數的第二個參數進行優化。
1.傳遞空數組,表示函數只執行一次!這就告訴 React 你的 effect 不依賴於 props 或 state 中的任何值,因此它永遠都不須要重複執行。effect 內部的 props 和 state 就會一直擁有其初始值。更接近你們熟悉的 componentDidMount 。
useEffect(()=>{ ... },[])
2.傳遞包含state的數組
會經過===比較count的前一次渲染的值與這一次要渲染的值,若是全等則React 會跳過這個 effect,這就實現了性能的優化。必定要將全部須要比較的state都放進數組,不然將不會進行比較,也就會直接跳過!
useEffect(()=>{ ... },[count])
react內置了多hook,咱們也能夠對這些hook進行業務方面的封裝,寫出屬於本身的hook!咱們將上面的疊加計時封裝成一個自定義hook:數字每秒疊加,而且修改顯示在title上。
import React , {useState,useEffect} from 'react' function useTitle(){//自定義hook let timer = null; const [count,setCount] = useState({name:'tom',age:11}) useEffect(()=>{ clearInterval(timer) timer = setInterval(()=>{ setCount({name:'tom',age:count.age+1}) },1000) document.title = count.age; return function(){ clearInterval(timer) } },[count]) return count;//返回一個對象 } function Item(){//自定義hook使用 const count = useTitle() return <> {`${count.name}已經${count.age}歲了`} </> }
將一些公共代碼提取出來,最後返回須要在頁面顯示的數據就成了
到這裏,react hook已經有所瞭解了吧!就是讓函數組件可以像class組件同樣自由自在的使用react的特性的函數。能夠不影響以前任何業務,在項目中徹底可選、100%向後兼容的react新特性!快在項目中用起來吧!
若有不妥!歡迎指正