react新特性hooks

react hooks可以使用在函數(shù)dom中,由于函數(shù)是沒有辦法像class那樣保存state假颇,所以可以用hooks的特性來保存state的狀態(tài)胚鸯。

1.useState和useEffect

useState接受一個(gè)初始值,并返回一個(gè)數(shù)值笨鸡,第一個(gè)是你要命名的該值姜钳,第二個(gè)是需要進(jìn)行更改該值得時(shí)候用的,類似于setState

useEffect相當(dāng)于componentDidMount而return的部分相當(dāng)于componentWillUnmount形耗,但也有點(diǎn)不同傲须,該方法接受第一個(gè)參數(shù)是函數(shù),第二個(gè)參數(shù)為一個(gè)數(shù)組趟脂,填寫當(dāng)哪個(gè)useState的值發(fā)生改變的時(shí)候泰讽,會執(zhí)行這useEffect里面的內(nèi)容。填寫一個(gè)[]則每次之后會加載頁面和離開頁面的時(shí)候執(zhí)行。
假如這里傳入了count已卸,則每次count發(fā)生變化都會執(zhí)行一次useEffect里面的代碼佛玄。
需要注意的是這里的setCount(c => c + 1) 最好不要變成setCount(count+1),因?yàn)樘钊肟諗?shù)組的時(shí)候,只會執(zhí)行一次useEffect累澡,而這時(shí)的count會根據(jù)當(dāng)時(shí)函數(shù)里面的count來決定是什么梦抢。這會引起閉包陷阱。所以最好的辦法是使用函數(shù)傳入count愧哟,然后返回一個(gè)新的count奥吩。

import React, { useState, useEffect } from 'react'

function myTimer() {
  const [count, setCount] = useState(0)
  useEffect(() => {
    console.log(count)
    const intenval = setInterval(() => {
      setCount(c => c + 1)
    }, 1000)
    return () => clearInterval(intenval)
  }, [])

  return <span>{count}</span>
}

export default myTimer

2.useReducer

useReducer類似于useState,當(dāng)改變的是一個(gè)對象之類的蕊梧,useReducer能使問題變得簡單霞赫。
useReducer接受一個(gè)方法,用來處理需要改變時(shí)候返回的state的函數(shù)肥矢,第二個(gè)是初始值端衰。返回的是一個(gè)初始值和一個(gè)dispacth方法。
然后只需要在改變的時(shí)候調(diào)用dispatch()傳入需要改變的狀態(tài)和數(shù)值即可甘改。由countReducer來進(jìn)行處理并返回一個(gè)新的count即可旅东。

import React, { useState, useReducer, useEffect } from 'react'

// 先聲明一個(gè)處理的方法
function countReducer(state, action) {
  switch (action.type) {
    case 'add':
      return state + 1
    case 'minus':
      return state - 1
    default:
      return state
  }
}

function myTimer() {
  // const [count, setCount] = useState(0)
  const [count, dispatchCount] = useReducer(countReducer, 0)
  useEffect(() => {
    const intenval = setInterval(() => {
      // setCount(c=>c+1)
      dispatchCount({ type: 'add' })
    }, 1000)
    return () => {
      console.log('didmount')
      return clearInterval(intenval)
    }
  }, [])

  return <span>{count}</span>
}

export default myTimer

3.uselayoutEffect

這個(gè)和useEffect有點(diǎn)相似,但這個(gè)是在Dom還沒渲染完成的時(shí)候執(zhí)行十艾,而useEffect是在渲染之后執(zhí)行的抵代。
一般進(jìn)行更改某些變量或者動畫都應(yīng)該在渲染之后再進(jìn)行執(zhí)行,所以這個(gè)使用得不是很多忘嫉。

4.useContext

類似于在全局當(dāng)中定義一個(gè)變量荤牍。方便祖先組件與后代組件(中間隔了好多層組件)傳值。
首先需要使用React.CreateContext()榄融,將其封裝成一個(gè)組件導(dǎo)出。

import React from 'react'
export default React.createContext('')

然后在父組件中引入該組件救湖。使用其提供的Provider組件將子組件包裹在一起愧杯。而value值就是需要傳給子組件的值了。這里的value可以用this.state.xxx來代替鞋既,當(dāng)發(fā)生改變的時(shí)候力九,子組件獲取到該值也會發(fā)生改變。

 <MyContext.Provider value="text" >
          <Component {...pageProps} />
  </MyContext.Provider>

在子組件中邑闺,就可以通過useContext(context component)來獲取了跌前,需要注意的是,必須是functional component陡舅,不然會報(bào)錯(cuò)抵乓。
通過useContext獲取到的值就是上面包裹的value的值

import React, { useState, useReducer, useEffect, useContext } from 'react'
import MyContext from '../lib/my-context'

function myTimer() {
  const context = useContext(MyContext)  
  const [count, dispatchCount] = useReducer(countReducer, 0)
  
  useEffect(() => {
    const intenval = setInterval(() => {
      // setCount(c=>c+1)
      dispatchCount({ type: 'add' })
    }, 1000)
    return () => {
      console.log('didmount')
      return clearInterval(intenval)
    }
  }, [])

  return (
    <span>
      {count}
      {context}
    </span>
  )
}

5.useRef

由于在函數(shù)組件中是不存在ref的。所以假如需要給函數(shù)組件里面擁有ref,就需要使用useRef了灾炭。
使用方法也很簡單茎芋,useRef()返回的就是一個(gè)ref,只需要將其放在想要獲取的dom上即可蜈出。

import React, {useState, useReducer,  useEffect, useContext, useRef} from 'react'
function myTimer() {
  const [count, dispatchCount] = useReducer(countReducer, 0)
  const context = useContext(MyContext)
  const spanRef = useRef()

  console.log(spanRef)
  return (
    <span ref={spanRef}>
      {count}
      {context}
    </span>
  )
}

6.useMeme,useCallback

主要是用于優(yōu)化性能的田弥。某些組件沒有更新的時(shí)候,不需要進(jìn)行更新操作铡原,就可以用到這兩個(gè)了。

首先我們將代碼更改一下,加入了一個(gè)Child的函數(shù)組件,Child中接受兩個(gè)參數(shù)割去,一個(gè)是button事件狞玛,用來更改count的值,一個(gè)是顯示count的值酌儒,input用來更改name的值辜妓。然后在Child中console一下,記錄每次Child更新的時(shí)間點(diǎn)忌怎。

function myCount() {
  const [name, setName] = useState('yiki')
  const [count, dispatchCount] = useReducer(countReducer, 0)

  const config = {
    text: `count is ${count}`,
    color: count > 3 ? 'red' : 'blue'
  }

  return (
    <div>
      <input value={name} onChange={e => setName(e.target.value)}></input>
      <Child
        onButtonClick={() => dispatchCount({ type: 'add' })}
        config={config}
      ></Child>
    </div>
  )
}

function Child({ onButtonClick, config }) {
  console.log('child render')
  return (
    <button onClick={onButtonClick} style={{ color: config.color }}>
      {config.text}
    </button>
  )
}
export default myTimer

可以發(fā)現(xiàn)籍滴,當(dāng)每次input輸入改變的時(shí)候,都會打印出child render榴啸。而這不是我們想要的孽惰。因?yàn)閏ount并沒有發(fā)生改變。Child組件是不需要發(fā)生改變的鸥印。這時(shí)候我們就需要用到useMemo和memo了勋功。
memo類似于class里面的shouldComponentUpdate,可以包裹在函數(shù)組件本身库说。由于該組件完全取決于onButtonClick和config狂鞋,所以只要這兩個(gè)數(shù)值不發(fā)生改變,那么Child組件就不會發(fā)生改變潜的。

import { memo } from 'react'
const Child = memo(function Child({ onButtonClick, config }) {
  console.log('child render')
  return (
    <button onClick={onButtonClick} style={{ color: config.color }}>
      {config.text}
    </button>
  )
})

但是骚揍,當(dāng)這樣寫完之后,會發(fā)現(xiàn)Child還是會發(fā)生改變啰挪,這是由于input會導(dǎo)致name發(fā)生改變信不,而name的改變會導(dǎo)致myCount組件也發(fā)生改變。而myCount一改變亡呵,config就會被重新聲明抽活。引用的地址就會改變,所以這時(shí)候Child還是會被重新渲染锰什。
這時(shí)候就需要用到useMemo了下硕。useMemo來判斷config是否改變丁逝,從而判斷是否要返回一個(gè)新的對象,這時(shí)候我們對config的聲明做一下修改卵牍。

import { useMemo } from 'react'
const config = useMemo(
    () => ({
      text: `count is ${count}`,
      color: count > 3 ? 'red' : 'blue'
    }),
    [count]
  )

第一個(gè)參數(shù)是一個(gè)方法果港,返回一個(gè)值,第二個(gè)參數(shù)和useEffect一樣糊昙,根據(jù)該值來判斷是否需要返回一個(gè)新的值辛掠。
但這時(shí)候還是不行,輸入input的時(shí)候释牺,Child還是渲染了萝衩。這是因?yàn)閛nButtonClick={() => dispatchCount({ type: 'add' })}傳入的是一個(gè)匿名函數(shù),每次myCount更新的時(shí)候地址也會改變没咙。
這時(shí)候可以用useMemo返回一個(gè)函數(shù)猩谊,或者用useCallback來進(jìn)行返回一個(gè)函數(shù)。第二個(gè)參數(shù)也是依賴項(xiàng)祭刚,根據(jù)某個(gè)值來判斷是否要更新牌捷。

import { useCallback } from 'react'
const handleButtonClick = useCallback(
    () => dispatchCount({ type: 'add' }),
    []
 )

假如使用useMemo就需要多包裹一層用來返回一個(gè)函數(shù)

const handleButtonClick = useMemo(
    () => () => dispatchCount({ type: 'add' }),
    []
  )

自此當(dāng)input值發(fā)生改變的時(shí)候Child就不會重新渲染,只有當(dāng)count的值發(fā)生改變的時(shí)候才會進(jìn)行重新渲染涡驮,完成了對Child的性能優(yōu)化了暗甥。

7.閉包陷阱

由于是函數(shù)組件,所以很容易出現(xiàn)閉包的情況捉捅,當(dāng)我們使用setTimeout來進(jìn)行延時(shí)調(diào)用來獲取某值的時(shí)候撤防,而該值在延時(shí)的時(shí)候發(fā)生了改變,但是setTimeout獲取到的其實(shí)是沒有改變的當(dāng)時(shí)的值棒口。

function MyCount(){
  const [count,setCount] = useState(0)
  return <>
  <button onClick={() => {setCount(c=>c+1)}}>{count}</button>
  <button onClick={()=>{setTimeout(()=>{alert(count)},2000)}}>延時(shí)獲取count</button>
</>
}

點(diǎn)擊延時(shí)后寄月,再在2秒內(nèi)點(diǎn)擊button2次,這時(shí)候彈出的值應(yīng)該是2无牵,但是由于閉包的關(guān)系漾肮,所以彈出來的是0。由于我們在使用class的時(shí)候茎毁,是通過this.state.count來進(jìn)行獲取的克懊,所以不會出現(xiàn)這個(gè)問題。但是在函數(shù)中充岛,往往很容易就會出現(xiàn)和現(xiàn)實(shí)不符的情況保檐。
這時(shí)候可以通過useRef來進(jìn)行解決耕蝉。由于useRef返回的都是同一個(gè)對象崔梗,所以可以將其值掛載到ref.current上,這樣就能獲取到最新的值了垒在。
這時(shí)候我們可以改成這種如下形式:

function MyCount(){
  const [count,setCount] = useState(0)
  const countRef = useRef()
  countRef.current = count
  return <>
  <button onClick={() => {setCount(c=>c+1)}}>{count}</button>
  <button onClick={()=>{setTimeout(()=>{alert(countRef.current)},2000)}}>延時(shí)獲取count</button>
</>
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蒜魄,一起剝皮案震驚了整個(gè)濱河市扔亥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谈为,老刑警劉巖旅挤,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異伞鲫,居然都是意外死亡粘茄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門秕脓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柒瓣,“玉大人,你說我怎么就攤上這事吠架≤狡叮” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵傍药,是天一觀的道長磺平。 經(jīng)常有香客問我,道長拐辽,這世上最難降的妖魔是什么拣挪? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮薛训,結(jié)果婚禮上媒吗,老公的妹妹穿的比我還像新娘。我一直安慰自己乙埃,他們只是感情好闸英,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著介袜,像睡著了一般甫何。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遇伞,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天辙喂,我揣著相機(jī)與錄音,去河邊找鬼鸠珠。 笑死巍耗,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的渐排。 我是一名探鬼主播炬太,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼驯耻!你這毒婦竟也來了亲族?” 一聲冷哼從身側(cè)響起炒考,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎霎迫,沒想到半個(gè)月后斋枢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡知给,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年瓤帚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涩赢。...
    茶點(diǎn)故事閱讀 38,626評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缘滥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谒主,到底是詐尸還是另有隱情朝扼,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布霎肯,位于F島的核電站擎颖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏观游。R本人自食惡果不足惜搂捧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望懂缕。 院中可真熱鬧允跑,春花似錦、人聲如沸搪柑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽工碾。三九已至弱睦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間渊额,已是汗流浹背况木。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旬迹,地道東北人火惊。 一個(gè)月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像奔垦,于是被迫代替她去往敵國和親屹耐。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評論 2 348

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