react hook和class的差別有哪些
區別:1、hooks的寫法比class簡潔;2、hooks的業務代碼比class更加聚合;3、class組件的邏輯復用通常用render props以及HOC兩種方式,而react hooks提供了自訂hooks來復用邏輯。
本教學操作環境:Windows7系統、react17.0.1版、Dell G3電腦。
react hooks與class元件有哪些不同?下面就來帶大家比較一下react hooks和class元件,聊聊它們的差別。
react-hooks解決的問題
#函數元件中不能擁有自己的狀態(state)。在hooks之前函數元件是無狀態的,都是透過props來取得父元件的狀態,但是hooks提供了useState來維護函數元件內部的狀態。
函數元件中不能監聽元件的生命週期。 useEffect聚合了多個生命週期函數。
class元件中生命週期較為複雜(在15版本到16版本的變化大)。
class元件邏輯難以重複使用(HOC,render props)。
hooks對比class的好處(對比)
1、寫法更加的簡潔
我們以最簡單的計數器為例:
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}>点击</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}>点击</button> </div> ) }
可以看到使用hooks的程式碼比起class元件程式碼更的簡潔、清晰。
2、業務程式碼更聚合
使用class元件經常會出現一個功能出現在兩個生命週期函數內的情況,這樣分開寫入有時候可能會忘記。例如:
let timer = null componentDidMount() { timer = setInterval(() => { // ... }, 1000) } // ... componentWillUnmount() { if (timer) clearInterval(timer) }
由於加入定時器和清除定時器是在兩個不同的生命週期函數,中間可能會有許多其他的業務程式碼,所以可能會忘記清除定時器,如果在元件卸載時沒有添加清楚定時器的函數可能會造成記憶體洩漏、網路一直請求等問題。
但是使用hooks可以讓程式碼更加的集中,方便我們管理,也不容易忘記:
useEffect(() => { let timer = setInterval(() => { // ... }, 1000) return () => { if (timer) clearInterval(timer) } }, [//...])
3、邏輯復用方便
class組件的邏輯復用通常用render props以及HOC兩種方式。 react hooks提供了自訂hooks來重複使用邏輯。
下面以取得滑鼠在頁面的位置的邏輯重複使用為例:
class元件render props方式重複使用
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方式復用
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對邏輯復用更加的方便,使用的時候邏輯也更加清晰。
hooks常見的一些API使用
1、useState
語法
#const [value, setValue] = useState(0)
這種語法方式是ES6的陣列結構,陣列的第一個值是宣告的狀態,第二個值是狀態的改變函數。
每一幀都有獨立的狀態
個人理解針對每一幀獨立的狀態是採用了閉包的方法來實現的。
function Example() { const [val, setVal] = useState(0) const timeoutFn = () => { setTimeout(() => { // 取得的值是点击按钮的状态,不是最新的状态 console.log(val) }, 1000) } return ( <> <p>{val}</p> <button onClick={()=>setVal(val+1)}>+</button> <button onClick={timeoutFn}>alertNumber</button> </> ) }
當元件的狀態或props更新時,該函數元件會被重新呼叫渲染,並且每一次的渲染都是獨立的都有自己獨立的props以及state,不會影響其他的渲染。
2、useEffect
語法
useEffect(() => { //handler function... return () => { // clean side effect } }, [//dep...])
useEffect接收一個回呼函數以及依賴項,當依賴項改變時才會執行裡面的回呼函數。 useEffect類似class組件didMount、didUpdate、willUnmount的生命週期函數。
注意點
useEffect是異步的在元件渲染完成後才會執行
useEffect的回呼函數只能傳回一個清除副作用的處理函數或不回傳
#如果useEffect傳入的依賴項是空數組那麼useEffect內部的函數只會執行一次
3、useMemo、useCallback
useMemo和useCallback主要用於減少元件的更新次數、最佳化元件效能的。
useMemo接收一個回呼函數以及依賴項,只有依賴項變更時才會重新執行回呼函數。
useCallback接收一個回呼函數以及依賴項,並且傳回該回呼函數的memorize版本,只有在依賴項重新變更時才會重新新的memorize版本。
語法
const memoDate = useMemo(() => data, [//dep...]) const memoCb = useCallback(() => {//...}, [//dep...])
在優化元件效能時針對class元件我們一般使用React.PureComponent,PureComponent會在shouldUpdate進行一次金錢比較,判斷是否需要更新;針對函數元件我們一般使用React.memo。但在使用react hooks時由於每個渲染更新都是獨立的(產生了新的狀態),即使使用了React.memo,還是會重新渲染。
比如下面这种场景,改变子组件的name值后由于父组件更新后每次都会生成新值(addAge函数会改变),所以子组件也会重新渲染。
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优化
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的依赖性发生变化时,才会重新生成memorize函数。所以当改变name的状态是addAge不会变化。
4、useRef
useRef类似于react.createRef。
const node = useRef(initRef)
useRef 返回一个可变的 ref 对象,其 current 属性被初始化为传入的参数(initRef)
作用在DOM上
const node = useRef(null) <input ref={node} />
这样可以通过node.current属性访问到该DOM元素。
需要注意的是useRef创建的对象在组件的整个生命周期内保持不变,也就是说每次重新渲染函数组件时,返回的ref 对象都是同一个(使用 React.createRef ,每次重新渲染组件都会重新创建 ref)。
5、useReducer
useReducer类似于redux中的reducer。
语法
const [state, dispatch] = useReducer(reducer, initstate)
useReducer传入一个计算函数和初始化state,类似于redux。通过返回的state我们可以访问状态,通过dispatch可以对状态作修改。
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> ) }
使用步骤
- 父组件创建并导出
context:export const MyContext = createContext()
- 父组件使用
provider
和value
提供值:<MyContext.provide value={{name: 'cc', age: 22}} />
- 子组件导入父组件的
context:import { MyContext } from './parent'
- 获取父组件提供的值:
const data = useContext(MyContext)
不过在多数情况下我们都不建议使用context
,因为会增加组件的耦合性。
7、useLayoutEffect
useEffect 在全部渲染完毕后才会执行;useLayoutEffect 会在 浏览器 layout之后,painting之前执行,并且会柱塞DOM;可以使用它来读取 DOM 布局并同步触发重渲染。
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之前执行,useEffect在painting之后执行。
hooks让函数组件拥有了内部状态、生命周期,使用hooks让代码更加的简介,自定义hooks方便了对逻辑的复用,并且摆脱了class组件的this问题;但是在使用hooks时会产生一些闭包问题,需要仔细使用。
【相关推荐:Redis视频教程】
以上是react hook和class的差別有哪些的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

熱門話題

類別和方法的概念和實例類別(Class):用來描述具有相同的屬性和方法的物件的集合。它定義了該集合中每個物件所共有的屬性和方法。物件是類別的實例。方法:類別中定義的函數。類別的建構方法__init__():類別有一個名為init()的特殊方法(建構方法),該方法在類別實例化時會自動呼叫。實例變數:在類別的宣告中,屬性是用變數來表示的,這種變數就稱為實例變量,實例變數就是用self修飾的變數。實例化:建立一個類別的實例,類別的具體物件。繼承:即一個派生類別(derivedclass)繼承基底類別(baseclass)的

class是python中的一個關鍵字,用來定義一個類,定義類別的方法:class後面加一個空格然後加類名;類名規則:首字母大寫,如果多個單字用駝峰命名法,如【class Dog()】。

jQuery是一種經典的JavaScript庫,被廣泛應用於網頁開發中,它簡化了在網頁上處理事件、操作DOM元素和執行動畫等操作。在使用jQuery時,常會遇到需要取代元素的class名稱的情況,本文將介紹一些實用的方法,以及具體的程式碼範例。 1.使用removeClass()和addClass()方法jQuery提供了removeClass()方法來刪除

在編寫PHP程式碼時,使用類別(Class)是一個非常常見的做法。透過使用類,我們可以將相關的功能和資料封裝在一個單獨的單元中,使程式碼更加清晰、易於閱讀和易於維護。本文將詳細介紹PHPClass的用法,並提供具體的程式碼範例,幫助讀者更好地理解如何在實際專案中應用類別來優化程式碼。 1.建立和使用類別在PHP中,可以使用關鍵字class來定義一個類,並在類別中定義屬性和方法。

Vue報錯:無法正確使用v-bind綁定class和style,怎麼解決?在Vue開發中,我們常常會用到v-bind指令來動態綁定class和style,但是有時候我們可能會遇到一些問題,如無法正確使用v-bind綁定class和style。在本篇文章中,我將為你解釋這個問題的原因,並提供解決方案。首先,讓我們先來了解v-bind指令。 v-bind用於將V

背景最近針對公司框架進行關鍵業務代碼進行加密處理,防止透過jd-gui等反編譯工具能夠輕鬆還原工程代碼,相關混淆方案配置使用比較複雜且針對springboot項目問題較多,所以針對class文件加密再通過自訂的classloder進行解密加載,此方案並不是絕對安全,只是加大反編譯的困難程度,防君子不防小人,整體加密保護流程圖如下圖所示maven插件加密使用自訂maven插件對編譯後指定的class檔案進行加密,加密後的class檔案拷貝到指定路徑,這裡是儲存到resource/corecla

jquery判斷元素是否有class的方法:1、透過「hasClass('classname')」方法判斷元素是否具有某個class;2、透過「is('.classname')」方法判斷元素是否具有某個class。
![解決'[Vue warn]: v-bind:class/ :class”錯誤的方法](https://img.php.cn/upload/article/000/465/014/169300902772563.jpg?x-oss-process=image/resize,m_fill,h_207,w_330)
解決「[Vuewarn]:v-bind:class/:class」錯誤的方法在使用Vue開發過程中,我們常常會遇到一些錯誤提示,其中一個常見的錯誤就是「[Vuewarn]:v-bind:class /:class”錯誤。這個錯誤提示通常出現在我們使用v-bind:class或:class屬性時,表示Vue無法正確解析我們設定的class值。那麼,如
