溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

react hook和class的區(qū)別是什么

發(fā)布時間:2022-03-22 13:32:47 來源:億速云 閱讀:550 作者:iii 欄目:web開發(fā)

這篇文章主要介紹“react hook和class的區(qū)別是什么”,在日常操作中,相信很多人在react hook和class的區(qū)別是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”react hook和class的區(qū)別是什么”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

區(qū)別:1、hooks的寫法比class簡潔;2、hooks的業(yè)務(wù)代碼比class更加聚合;3、class組件的邏輯復(fù)用通常用render props以及HOC兩種方式,而react hooks提供了自定義hooks來復(fù)用邏輯。

本教程操作環(huán)境:Windows7系統(tǒng)、react17.0.1版、Dell G3電腦。

react hooks與class組件有哪些區(qū)別?下面就來帶大家對比一下react hooks和class組件,聊聊它們的區(qū)別。

react-hooks解決的問題

  • 函數(shù)組件中不能擁有自己的狀態(tài)(state)。在hooks之前函數(shù)組件是無狀態(tài)的,都是通過props來獲取父組件的狀態(tài),但是hooks提供了useState來維護(hù)函數(shù)組件內(nèi)部的狀態(tài)。

  • 函數(shù)組件中不能監(jiān)聽組件的生命周期。useEffect聚合了多個生命周期函數(shù)。

  • class組件中生命周期較為復(fù)雜(在15版本到16版本的變化大)。

  • class組件邏輯難以復(fù)用(HOC,render props)。

hooks對比class的好處(對比)

1、寫法更加的簡潔

我們以最簡單的計數(shù)器為例:

class組件

class ExampleOfClass extends Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 1
    }
  }
  handleClick = () => {
    let { count } = this.state
    this.setState({
      count: count+1
    })
  }
  render() {
    const { count } = this.state
    return (
      <div>
        <p>you click { count }</p>
        <button onClick={this.handleClick}>點(diǎn)擊</button>
      </div>
    )
  }
}

hooks

function ExampleOfHooks() {
    const [count, setCount] = useState(0)
    const handleClick = () => {
        setCount(count + 1)
    }
    return (
      <div>
        <p>you click { count }</p>
        <button onClick={handleClick}>點(diǎn)擊</button>
      </div>
    )
}

可以看到使用hooks的代碼相比class組件代碼更加的簡潔、清晰。

2、業(yè)務(wù)代碼更加聚合

使用class組件經(jīng)常會出現(xiàn)一個功能出現(xiàn)在兩個生命周期函數(shù)內(nèi)的情況,這樣分開寫有時候可能會忘記。比如:

let timer = null
componentDidMount() {
    timer = setInterval(() => {
        // ...
    }, 1000)
}
// ...
componentWillUnmount() {
    if (timer) clearInterval(timer)
}

由于添加定時器和清除定時器是在兩個不同的生命周期函數(shù),中間可能會有很多其他的業(yè)務(wù)代碼,所以可能會忘記清除定時器,如果在組件卸載時沒有添加清楚定時器的函數(shù)就可能會造成內(nèi)存泄漏、網(wǎng)絡(luò)一直請求等問題。

但是使用hooks可以讓代碼更加的集中,方便我們管理,也不容易忘記:

useEffect(() => {
    let timer = setInterval(() => {
        // ...
    }, 1000)
    return () => {
        if (timer) clearInterval(timer)
    }
}, [//...])

3、邏輯復(fù)用方便

class組件的邏輯復(fù)用通常用render props以及HOC兩種方式。react hooks提供了自定義hooks來復(fù)用邏輯。

下面以獲取鼠標(biāo)在頁面的位置的邏輯復(fù)用為例:

class組件render props方式復(fù)用

import React, { Component } from 'react'

class MousePosition extends Component {
  constructor(props) {
    super(props)
    this.state = {
      x: 0,
      y: 0
    }
  }

  handleMouseMove = (e) => {
    const { clientX, clientY } = e
    this.setState({
      x: clientX,
      y: clientY
    })
  }

  componentDidMount() {
    document.addEventListener('mousemove', this.handleMouseMove)
  }

  componentWillUnmount() {
    document.removeEventListener('mousemove', this.handleMouseMove)
  }

  render() {
    const { children } = this.props
    const { x, y } = this.state
    return(
      <div>
        {
          children({x, y})
        }
      </div>
    )
  }

}

// 使用
class Index extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (
      <MousePosition>
        {
          ({x, y}) => {
            return (
              <div>
                <p>x:{x}, y: {y}</p>
              </div>
            )
          }
        }
      </MousePosition>
    )
  }
}

export default Index

自定義hooks方式復(fù)用

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

function usePosition() {
  const [x, setX] = useState(0)
  const [y, setY] = useState(0)

  const handleMouseMove = (e) => {
    const { clientX, clientY } = e
    setX(clientX)
    setY(clientY)
  } 

  useEffect(() => {
    document.addEventListener('mousemove', handleMouseMove)
    return () => {
      document.removeEventListener('mousemove', handleMouseMove)
    }
  })
  return [
    {x, y}
  ]
}

// 使用
function Index() {
  const [position] = usePosition()
  return(
    <div>
      <p>x:{position.x},y:{position.y}</p>
    </div>
  )
}

export default Index

可以很明顯的看出使用hooks對邏輯復(fù)用更加的方便,使用的時候邏輯也更加清晰。

hooks常見的一些API使用

1、useState

語法

const [value, setValue] = useState(0)

這種語法方式是ES6的數(shù)組結(jié)構(gòu),數(shù)組的第一個值是聲明的狀態(tài),第二個值是狀態(tài)的改變函數(shù)。

每一幀都有獨(dú)立的狀態(tài)

個人理解針對每一幀獨(dú)立的狀態(tài)是采用了閉包的方法來實現(xiàn)的。

function Example() {
  const [val, setVal] = useState(0)
  const timeoutFn = () => {
      setTimeout(() => {
        // 取得的值是點(diǎn)擊按鈕的狀態(tài),不是最新的狀態(tài)
          console.log(val)
      }, 1000)
  }
  return (
      <>
          <p>{val}</p>
          <button onClick={()=>setVal(val+1)}>+</button>
          <button onClick={timeoutFn}>alertNumber</button>
      </>
  )
}

當(dāng)組件的狀態(tài)或者props更新時,該函數(shù)組件會被重新調(diào)用渲染,并且每一次的渲染都是獨(dú)立的都有自己獨(dú)立的props以及state,不會影響其他的渲染。

2、useEffect

語法

useEffect(() => {
    //handler function...
    
    return () => {
        // clean side effect
    }
}, [//dep...])

useEffect接收一個回調(diào)函數(shù)以及依賴項,當(dāng)依賴項發(fā)生變化時才會執(zhí)行里面的回調(diào)函數(shù)。useEffect類似于class組件didMount、didUpdate、willUnmount的生命周期函數(shù)。

注意點(diǎn)

  • useEffect是異步的在組件渲染完成后才會執(zhí)行

  • useEffect的回調(diào)函數(shù)只能返回一個清除副作用的處理函數(shù)或者不返回

  • 如果useEffect傳入的依賴項是空數(shù)組那么useEffect內(nèi)部的函數(shù)只會執(zhí)行一次

3、useMemo、useCallback

useMemo和useCallback主要用于減少組件的更新次數(shù)、優(yōu)化組件性能的。

  • useMemo接收一個回調(diào)函數(shù)以及依賴項,只有依賴項變化時才會重新執(zhí)行回調(diào)函數(shù)。

  • useCallback接收一個回調(diào)函數(shù)以及依賴項,并且返回該回調(diào)函數(shù)的memorize版本,只有在依賴項重新變化時才會重新新的memorize版本。

語法

const memoDate = useMemo(() => data, [//dep...])
const memoCb = useCallback(() => {//...}, [//dep...])

在優(yōu)化組件性能時針對class組件我們一般使用React.PureComponent,PureComponent會在shouldUpdate進(jìn)行一次錢比較,判斷是否需要更新;針對函數(shù)組件我們一般使用React.memo。但是在使用react hooks時由于每一次渲染更新都是獨(dú)立的(生成了新的狀態(tài)),即使使用了React.memo,也還是會重新渲染。

比如下面這種場景,改變子組件的name值后由于父組件更新后每次都會生成新值(addAge函數(shù)會改變),所以子組件也會重新渲染。

function Parent() {
  const [name, setName] = useState('cc')
  const [age, setAge] = useState(22)

  const addAge = () => {
    setAge(age + 1)
  }

  return (
    <>
      <p>父組件</p>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <p>age: {age}</p>
      <p>-------------------------</p>
      <Child addAge={addAge} />
    </>
  )
}

const Child = memo((props) => {
  const { addAge } = props
  console.log('child component update')
  return (
    <>
      <p>子組件</p>
      <button onClick={addAge}>click</button>
    </>
  )
})

使用useCallback優(yōu)化

function Parent() {
  const [name, setName] = useState('cc')
  const [age, setAge] = useState(22)

  const addAge = useCallback(() => {
    setAge(age + 1)
  }, [age])

  return (
    <>
      <p>父組件</p>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <p>age: {age}</p>
      <p>-------------------------</p>
      <Child addAge={addAge} />
    </>
  )
}

const Child = memo((props) => {
  const { addAge } = props
  console.log('child component update')
  return (
    <>
      <p>子組件</p>
      <button onClick={addAge}>click</button>
    </>
  )
})

只有useCallback的依賴性發(fā)生變化時,才會重新生成memorize函數(shù)。所以當(dāng)改變name的狀態(tài)是addAge不會變化。

4、useRef

useRef類似于react.createRef。

const node = useRef(initRef)

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

作用在DOM上

const node = useRef(null)
<input ref={node} />

這樣可以通過node.current屬性訪問到該DOM元素。

需要注意的是useRef創(chuàng)建的對象在組件的整個生命周期內(nèi)保持不變,也就是說每次重新渲染函數(shù)組件時,返回的ref 對象都是同一個(使用 React.createRef ,每次重新渲染組件都會重新創(chuàng)建 ref)。

5、useReducer

useReducer類似于redux中的reducer。

語法

const [state, dispatch] = useReducer(reducer, initstate)

useReducer傳入一個計算函數(shù)和初始化state,類似于redux。通過返回的state我們可以訪問狀態(tài),通過dispatch可以對狀態(tài)作修改。

const initstate = 0;
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {number: state.number + 1};
    case 'decrement':
      return {number: state.number - 1};
    default:
      throw new Error();
  }
}
function Counter(){
    const [state, dispatch] = useReducer(reducer, initstate);
    return (
        <>
          Count: {state.number}
          <button onClick={() => dispatch({type: 'increment'})}>+</button>
          <button onClick={() => dispatch({type: 'decrement'})}>-</button>
        </>
    )
}

6、useContext

通過useContext我們可以更加方便的獲取上層組件提供的context。

父組件

import React, { createContext, Children } from 'react'
import Child from './child'

export const MyContext = createContext()

export default function Parent() {

  return (
    <div>
      <p>Parent</p>
      <MyContext.Provider value={{name: 'cc', age: 21}}>
        <Child />
      </MyContext.Provider>
    </div>
  )
}

子組件

import React, { useContext } from 'react'
import { MyContext } from './parent'

export default function Parent() {
  const data = useContext(MyContext) // 獲取父組件提供的context
  console.log(data)
  return (
    <div>
      <p>Child</p>
    </div>
  )
}

使用步驟

  • 父組件創(chuàng)建并導(dǎo)出context:export const MyContext = createContext()

  • 父組件使用providervalue提供值:<MyContext.provide value={{name: 'cc', age: 22}} />

  • 子組件導(dǎo)入父組件的context:import { MyContext } from './parent'

  • 獲取父組件提供的值:const data = useContext(MyContext)

不過在多數(shù)情況下我們都不建議使用context,因為會增加組件的耦合性。

7、useLayoutEffect

useEffect 在全部渲染完畢后才會執(zhí)行;useLayoutEffect 會在 瀏覽器 layout之后,painting之前執(zhí)行,并且會柱塞DOM;可以使用它來讀取 DOM 布局并同步觸發(fā)重渲染。

export default function LayoutEffect() {
  const [color, setColor] = useState('red')
  useLayoutEffect(() => {
      alert(color) // 會阻塞DOM的渲染
  });
  useEffect(() => {
      alert(color) // 不會阻塞
  })
  return (
      <>
        <div id="myDiv" style={{ background: color }}>顏色</div>
        <button onClick={() => setColor('red')}>紅</button>
        <button onClick={() => setColor('yellow')}>黃</button>
      </>
  )
}

上面的例子中useLayoutEffect會在painting之前執(zhí)行,useEffect在painting之后執(zhí)行。

hooks讓函數(shù)組件擁有了內(nèi)部狀態(tài)、生命周期,使用hooks讓代碼更加的簡介,自定義hooks方便了對邏輯的復(fù)用,并且擺脫了class組件的this問題;但是在使用hooks時會產(chǎn)生一些閉包問題,需要仔細(xì)使用。

到此,關(guān)于“react hook和class的區(qū)別是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI