Hooks API你了解多少

useState

1、定義:useState是用來代替class Component里的state

2饲嗽、useState的性質(zhì)

  • useState的初始值只在第一次有效,這要特別注意不然容易采坑深胳,下面看個例子:

    import React, { useState,memo } from 'react'
    const Child =memo(({data})=>{
      const [name,setName]=useState(data);
      return(
          <div>
              <div>Child</div>
              <div>{name}------{data}</div>
          </div>
      )
    })
    function Index(params) {
      const [count, setCount] = useState(0)
        const [name, setName] = useState('mike')
      return(
          <div>
              <div>
                  {count}
              </div>
              <button onClick={()=>setCount(count+1)}>update count </button>
              <button onClick={()=>setName('jack')}>update name </button>
              <Child data={name}/>
          </div>
      )
    } 
    export default Index;
    

當我點擊update name時會更新父組件中的值同時name也會在子組件中顯示融虽,但是子組件中的name還是一開始的mike,這說明useState只在初次渲染被使用一次荔茬。這里還要注意一點的就是如果你的初始state需要通過復(fù)雜的計算來獲得废膘,則可以傳入一個函數(shù),在函數(shù)中計算并返回初始state慕蔚,此函數(shù)也只會在初次渲染時被調(diào)用丐黄。

  • 每次渲染都是獨立的閉包:

    這句話怎么理解呢?簡單點說就是每次渲染都有自己獨立的Props孔飒、State和事件處理函數(shù)灌闺,這樣就意味著每次更新狀態(tài)的時候函數(shù)組件就會重新被調(diào)用,那么每次渲染就是獨立的坏瞄,取到的值不會受到后面操作的影響桂对。

    function Index(params) {
      let [count, setCount]=useState(0);
      function showCount(){
          setTimeout(()=>{
              console.log(count)
          },3000)
      }
      return (
          <div>
              <div>{count}</div>
              <button onClick={()=>{setCount(count+1)}}>change count</button>
              <button onClick={showCount}>click show count</button>
          </div>
      )
    } 
    

3翼馆、useState和setState差異

在setState的時候纹磺,我們可以只修改state中的局部變量,而不需要將整個修改后的state傳進去怕膛,舉個例子:

//簡單點我就直接寫重點
this.state = {
    count: 0,
    age: 18,
}

handleClick = () => {
    // 我們只需要傳入修改的局部變量
    this.setState({
      count: 1
    });
}

而使用useState后缀棍,我們修改state必須將整個修改后的state傳入去宅此,因為它會直接覆蓋之前的state,而不是合并之前state對象爬范。

const [data, setData] = useState({
    count: 0,
    name: 'cjg',
    age: 18,
});

const handleClick = () => {
    const { count } = data;
    // 這里必須將完整的state對象傳進去
    setData({
      ...data,
      count: count + 1,
    })
};

useEffect

定義:useEffect被稱為副作用父腕,指那些沒有發(fā)生在數(shù)據(jù)向視圖轉(zhuǎn)化過程中的邏輯,比如ajax請求青瀑、訪問原生dom璧亮、本地持久化緩存痢法、綁定/解綁事件、添加訂閱杜顺,設(shè)置定時器等

使集集合:

1、作為只在第一次使用的 componentDidMount 蘸炸,用來異步請求數(shù)據(jù)

//將useEffect的第二個參數(shù)設(shè)置為[]
useEffect(()=>{
    const list = fetch(...);
},[])

2躬络、作為每次更新都會使用的 componentDidUpdate

//不設(shè)置useEffect的第二個參數(shù)
useEffect(()=>{
    const list = fetch(...);
})

3、作為監(jiān)聽器去監(jiān)聽某個值或者某些值的變化搭儒,然后執(zhí)行相應(yīng)的操作穷当。

//將userEffect的第二個參數(shù)設(shè)為一個數(shù)組,在這個數(shù)組中添加想要監(jiān)聽的值
useEffect(()=>{
    const list = name
},[name])

4淹禾、作為組件消亡時調(diào)用的 componentWillUnmount 馁菜,這里可以去取消一些

//在useEffect的最后返回一個回調(diào)函數(shù)
useEffect(() => {
    let timer=setTimeout()
    return () => {
        clearTimeout(timer)
    }
},[])

為什么要取消訂閱,因為每次render都會執(zhí)行一次useEffect铃岔,正如我上面的例子汪疮,如果每次render都去設(shè)置一個定時器,這是一個很龐大的開銷毁习,所以每次render之前都要把上一次的定時器clear智嚷。

useEffect的潛規(guī)則

  • useEffect中每一次使用的state值都固定在useEffect內(nèi)部,不會改變纺且,除非useEffect刷新來獲得最新的值盏道。

    const [count, setCount] = useState(0)
    useEffect(() => {
      console.log('use effect...',count)
      const timer = setInterval(() => {
      console.log('timer...count:', count)
      setCount(count + 1)
    }, 1000)
      return ()=> clearInterval(timer)
    },[])
        
    
  • useEffect不能被判斷語句包裹

    const [count, setCount] = useState(0)
    if(2 < 5){
       useEffect(() => {
              ....
        }) 
    }
    
    
  • useEffect不能被打斷

useRef

1、useref返回一個可變的ref對象载碌, 其 current 屬性被初始化為傳入的參數(shù)

const refContainer = useRef(initialValue);

useref返回的ref對象在組件的整個生命周期內(nèi)保持不變猜嘱,也就是說每次重新渲染函數(shù)組件時,返回的ref對象都是同一個(使用React.creatRef嫁艇,每次重新渲染組件都會重新創(chuàng)建ref)再看看上面那個例子

const [count, setCount] = useState(0)
const countRef = useRef(0)
useEffect(() => {
    console.log('use effect...',count)
    const timer = setInterval(() => {
        console.log('timer...count:', countRef.current)
        setCount(++countRef.current)
    }, 1000)
    return ()=> clearInterval(timer)
},[])

這就能正常顯示朗伶。

2、類組件步咪、react元素用React.creatRef腕让,函數(shù)式組件使用useRef來操作DOM

function Index(params) {
    const [count, setCount] = useState(0);
    const btnRef=useRef(null);
    useEffect(()=>{
        console.log("use effect");
        console.log(btnRef.current)
        const onClick=()=>{
            setCount(count+1);
        }
        btnRef.current.addEventListener('click',onClick,false);
        return ()=>{
            btnRef.current.removeEventListener('click',onClick,false)
        }
    },[count])
    return(
        <div>
            <div>
                {count}
            </div>
            <button ref={btnRef}>click</button>
        </div>
    )
}   

最后別忘了取消事件綁定,這樣就可以獲取到我們想要的DOM值歧斟,同時給相應(yīng)的DOM添加事件纯丸。

memo

1、定義:memo對標類組件中的PureComponent静袖,可以減少重新render的次數(shù)觉鼻。

先看一個例子

function Child({name}){
    console.log("this is Child");
    return(
        <div>{name}</div>
    )
}

function Index(params) {
    const [count,setCount]=useState(0);
    return(
        <div>
            <div>{count}</div>
            <button onClick={()=>{setCount(count+1)}}>click</button>
            <Child name="小明"></Child>
        </div>
    )
}

首先第一次渲染會正常打印出"this is Child",然后我們點擊click按鈕改變count,這樣父組件中的count就會更新,但是問題來了我子組件也會重新渲染队橙,你可能會想傳遞給子組件的props沒有變坠陈,要是子組件不重新渲染就好了萨惑,為什么會這么想呢?我們可以假設(shè)一下我們抽出去的子組件是一個非常龐大的組件仇矾,渲染一次會消耗很多性能庸蔼,那么我們應(yīng)該盡量減少這個組件的渲染,那么我們該怎么實現(xiàn)呢贮匕?答案就是memo姐仅,在給定相同props的情況下渲染相同的結(jié)果,但是這里要注意一個問題就是memo是淺比較刻盐,意思就是對象只會比較內(nèi)存地址掏膏,只要內(nèi)存地址沒變,管你對象中的值怎么變化都不會觸發(fā)render敦锌。

下面來包裝一下上面那個例子馒疹。

const Child=memo(({name})=>{
    console.log("this is Child");
    return(
        <div>{name}</div>
    )
})

現(xiàn)在就實現(xiàn)了上面的功能。

memo高級用法:

默認情況下只會對props的對象進行淺層比較(淺層比較就是只會對比前后兩次props對象的引用是否相同乙墙,不會對比對象里面的內(nèi)容是否相同)如果想自己控制比較的過程那就需要自定義比較函數(shù)颖变,通過第二個參數(shù)傳入來實現(xiàn),下面是一個簡單的例子:

function areEqual(prevProps,nextProps){
    return prevProps.data!==nextProps.data
}
const Child=memo((props)=>{
    return(
        <div>{props.data.count}</div>
    )
},areEqual)

function Index(params) {
    const [count,setCount]=useState(0);
    const useCount=useRef({count:1})
    const Countt=useRef(2)
    useEffect(()=>{
        let timer=setInterval(()=>{
            setCount(++useCount.current.count)
        },1000)
        return ()=>{
            clearInterval(timer)
        }
    },[])
    return(
        <div>
            <div>{count}</div>
            <button onClick={()=>{Countt.current++}}>click</button>
            <Child data={useCount.current}></Child>
        </div>
    )
)

如果不加上判斷函數(shù)听想,子組件就不會去更新悼做,因為每次傳入的對象地址沒有變,但是對象中的數(shù)據(jù)發(fā)生改變哗魂。

useMemo

前面介紹的memo是用來減少render次數(shù)肛走,那接下來介紹的就是用來減少計算次數(shù)。

先看一個例子:

const Child=memo((props)=>{
    console.log("this is Child")
    return(
        <div>{props.data.name}</div>
    )
})

function Index(params) {
    const [count,setCount]=useState(0);
    const [name,setName]=useState("小紅")
    const data={
        name
    }
    return(
        <div>
            <div>{count}</div>
            <button onClick={()=>{setCount(count+1)}}>click</button>
            <Child data={data}></Child>
        </div>
    )
}

這個例子和上面memo講的例子有點相似录别,這個例子中我們每點擊一次父組件就會重新渲染一次朽色,那么就會重新生成一個data對象,這個對象和上次的對象地址是不一樣的组题,所以子組件就檢測到了葫男,他發(fā)現(xiàn)你父組件傳過來的對象地址不一樣就會判斷上一次和這一次的對象是不一樣的然后就會刷新一次,但是這兩次對象的值是一樣的我再去重新渲染一次子組件實在是浪費性能崔列,所以又該怎么解決呢梢褐?有人可能會結(jié)合上面那個例子,把這個對象定義在useRef中赵讯,然后再去定義一個判斷函數(shù)不就行了盈咳,這樣是可以實現(xiàn)我們的功能,但是再想想有沒有什么方法在父組件就給我們的對象做處理边翼,如果傳入子組件的對象沒有改變就不去重新生成一個新的對象鱼响,useMemo這個API就出現(xiàn)了,我們看看他是怎么工作的组底。

usememo 有著暫存的能力丈积,會記住一些值筐骇,在重新渲染時會將依賴數(shù)組中的值取出來和上一次記錄的值進行比較, 如果不相等才會重新執(zhí)行回調(diào)函數(shù)江滨,否則直接返回記住的值 铛纬,對于這個例子沒有新的對象就沒有新的地址。

下面是改進后的:

const Child=memo((props)=>{
    console.log("this is Child")
    return(
        <div>{props.data.name}</div>
    )
})

function Index(params) {
    const [count,setCount]=useState(0);
    const [name,setName]=useState("小紅")
    const data=useMemo(()=>{
        return {
            name
        }
    },[name])
    return(
        <div>
            <div>{count}</div>
            <button onClick={()=>{setCount(count+1)}}>click</button>
            <Child data={data}></Child>
        </div>
    )
}

最后要提醒一下唬滑,如果沒有給usememo提供依賴組告唆,他就會在每次渲染時都會重新計算新的值。

useCallback

usememouseCallback相似间雀,都有著緩存的作用,但是他們本質(zhì)的區(qū)別就在于一個是緩存值的镊屎,一個是緩存函數(shù)的惹挟,這里要注意一下useCallback有沒有后面的依賴很重要,如果沒有依賴缝驳,每次渲染還是會重新生成新的函數(shù)连锯。來看個例子:

const Child=memo((props)=>{
    console.log("this is Child")
    return(
        <div>
            <div>{props.name}</div>
            <button onClick={props.click}>click child</button>
        </div>
    )
})

function Index(params) {
    const [count,setCount]=useState(0);
    const [age,setAge]=useState(12)
    const callback=()=>{
        setAge(age+1);
    }
    return(
        <div>
            <div>{count}</div>
            <button onClick={()=>{setCount(count+1)}}>click</button>
            <Child name="小明" click={callback}></Child>
        </div>
    )
}

我們來按操作一遍,頁面首次渲染頁面正常顯示同時也會打印出this is Child用狱,當我們再次點擊父組件中的按鈕运怖,父組件中的count改變,但是控制臺又會打印一次this is Child夏伊,說明子組件有重新渲染了一次摇展,但是子組件卻沒有什么變化,也沒有去觸發(fā)子組件中的事件溺忧,說明這次渲染是多余的咏连。那原因是什么呢?這其實我們呢上面也說過了鲁森,函數(shù)是組建每次渲染函數(shù)組件都會重頭開始重新執(zhí)行祟滴,那么這兩次創(chuàng)建的callback函數(shù)肯定發(fā)生變化了,所以導(dǎo)致子組件重新渲染歌溉。

然后就進入主題了垄懂,使用我們的useCallback方法,改進下代碼

const Child=memo((props)=>{
    console.log("this is Child")
    return(
        <div>
            <div>{props.name}</div>
            <button onClick={props.click}>click child</button>
        </div>
    )
})

function Index(params) {
    const [count,setCount]=useState(0);
    const [age,setAge]=useState(12)
    const callback=useCallback(()=>{
        setAge(age+1);
    },[age])
    return(
        <div>
            <div>{count}</div>
            <button onClick={()=>{setCount(count+1)}}>click</button>
            <Child name="小明" click={callback}></Child>
        </div>
    )
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末痛垛,一起剝皮案震驚了整個濱河市草慧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌匙头,老刑警劉巖冠蒋,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異乾胶,居然都是意外死亡抖剿,警方通過查閱死者的電腦和手機朽寞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斩郎,“玉大人脑融,你說我怎么就攤上這事∷跻耍” “怎么了肘迎?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長锻煌。 經(jīng)常有香客問我妓布,道長,這世上最難降的妖魔是什么宋梧? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任匣沼,我火速辦了婚禮,結(jié)果婚禮上捂龄,老公的妹妹穿的比我還像新娘释涛。我一直安慰自己,他們只是感情好倦沧,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布唇撬。 她就那樣靜靜地躺著,像睡著了一般展融。 火紅的嫁衣襯著肌膚如雪窖认。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天告希,我揣著相機與錄音耀态,去河邊找鬼。 笑死暂雹,一個胖子當著我的面吹牛首装,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播杭跪,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼仙逻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涧尿?” 一聲冷哼從身側(cè)響起系奉,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎姑廉,沒想到半個月后缺亮,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡桥言,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年萌踱,在試婚紗的時候發(fā)現(xiàn)自己被綠了葵礼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡并鸵,死狀恐怖鸳粉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情园担,我是刑警寧澤届谈,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站弯汰,受9級特大地震影響艰山,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜咏闪,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一曙搬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧汤踏,春花似錦织鲸、人聲如沸舔腾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稳诚。三九已至哗脖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扳还,已是汗流浹背才避。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留氨距,地道東北人桑逝。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像俏让,于是被迫代替她去往敵國和親楞遏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容