react中hook介紹以及使用教程
前言
最近由于公司的項(xiàng)目開發(fā),就學(xué)習(xí)了在react關(guān)于hook的使用,對(duì)其有個(gè)基本的認(rèn)識(shí)以及如何在項(xiàng)目中去應(yīng)用hook。在這篇博客中主要從以下的幾個(gè)點(diǎn)進(jìn)行介紹:
- hook簡(jiǎn)介
- hook中常用api的使用
- hook在使用過程中需要去注意的地方
- hook中怎樣去實(shí)現(xiàn)class組件中的聲明周期函數(shù)
hook
首先介紹關(guān)于hook的含義,以及其所要去面對(duì)的一些場(chǎng)景
含義:Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性。簡(jiǎn)單來(lái)說就是可以使用函數(shù)組件去使用react中的一些特性
所要解決的問題:
- 解決組件之間復(fù)用狀態(tài)邏輯很難得問題,hook能解決的就是在你無(wú)需修改之前組件結(jié)構(gòu)的情況下復(fù)用狀態(tài)邏輯,在不使用hook的情況下,需要使用到一些高級(jí)的用法如高級(jí)組件、provider、customer等,這種方式對(duì)于新手來(lái)說不太友好,可能在理解上就比較的困難
- 對(duì)于復(fù)雜組件可以去拆分其邏輯,例如在你使用生命周期函數(shù)時(shí),不同的生命周期需要在不同的時(shí)刻進(jìn)行,因此在此時(shí)對(duì)于復(fù)雜的組件來(lái)說,有的生命周期函數(shù)中就存在大量的邏輯,在可讀性上面就大打折扣。當(dāng)使用hook時(shí),就可以進(jìn)行組件邏輯的劃分,將相同的邏輯給整合在一起,這樣就大大增加可讀性也在一方面利于維護(hù)
- 不需要對(duì)于class組件的理解,當(dāng)你在最初去學(xué)習(xí)時(shí),你不得不去理解this這個(gè)關(guān)鍵字,在當(dāng)前組件所表示的含義,但是在hook中就不需要。能夠解決你在不使用class組件的情況下去體現(xiàn)react的特性
- 需要注意的一點(diǎn)就是hook和class組件是不能夠同時(shí)使用的,在實(shí)際的使用過程中一定要注意,否則就會(huì)出現(xiàn)報(bào)錯(cuò)
那么接下來(lái)所要介紹的部分就是如何去使用hook
state hook
對(duì)于使用過class組件的同學(xué),相信對(duì)于state肯定有很深的印象,對(duì)于一些需要用到的全局變量,在class組件中我們常常采用的方式是this.state = {},但是在hook中我們采用的方式就是使用useState這個(gè)hook,然后就可以對(duì)這種全局變量進(jìn)行引用,在引用時(shí)只需要用其變量名即可,這里就拿官網(wǎng)的例子來(lái)舉例:
import React, { useState } from 'react';
import React, { useState } from 'react'; function Example() { // 聲明一個(gè)叫 "count" 的 state 變量 const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
在上面的這個(gè)例子中,我們?cè)O(shè)置變量方式采用的就是const [count, setCount] = useState(0)這種方式,其中的0就是給count賦初值為0,如果想要給count賦值為一個(gè)空對(duì)象,那么只需要const [count, setCount] = useState({}),這樣的方式就行了,那么這樣你在用count時(shí),此時(shí)獲取到的值就為一個(gè)空對(duì)象。
作用:返回一個(gè)state,以及更新state的函數(shù)
- 函數(shù)式更新:新的state需要通過使用先前的state計(jì)算得出,將函數(shù)傳遞給setState,該函數(shù)將接收先前的state,并返回一個(gè)更新后的值
- 惰性初始state,initialState參數(shù)只會(huì)在組件的初始渲染中起作用,如果初始化state需要通過一個(gè)復(fù)雜計(jì)算來(lái)獲取,則可以傳入一個(gè)函數(shù),在函數(shù)中計(jì)算并返回初始的state,此函數(shù)只在初始渲染時(shí)被掉用,如下所示:
const [state, setState] = useState(() => { const initialState = someExpensiveComputation(props); return initialState; })
在hook中如何給全局變量設(shè)置值
在class組件中我們給放在state中的變量賦值時(shí),通常采用的方式就是this.setState()這種方式,那么在hook中所要采用的就是set+變量名這種方式,如
const [count, setCount] = useState(0)
在這里通過上面我們已經(jīng)知道的就是count能夠獲取到值,那么其所對(duì)應(yīng)的setCount(值),這種賦值的方式就是給count變量賦值的,然后通過count就能夠獲取到值。
- 為什么要采用這種方式呢?
- 原因:是因?yàn)閞eact中的單向數(shù)據(jù)源,這樣的話,能夠保證你的數(shù)據(jù)源流向會(huì)更加的清楚,這也是react所區(qū)別于vue中雙向數(shù)據(jù)源綁定的一點(diǎn)
hook中設(shè)置多個(gè)全局變量的方式
在hook中,如果我們需要去設(shè)置多個(gè)類似于上面所說的count,那么就需要多次使用useState這個(gè)hook,當(dāng)然你也可以設(shè)置一個(gè)變量在hook的最外部,即在hook這個(gè)函數(shù)組件的外部。需要注意的是別在整個(gè)hook這個(gè)函數(shù)的全局設(shè)置,因此在hook的運(yùn)行機(jī)制中,在每次加載時(shí),都會(huì)從新去加載里面的變量,因此你是不能夠去獲取到在整個(gè)函數(shù)內(nèi)部中使用該變量所改變的值的,能夠獲取到的就只是這個(gè)變量的初始值*
useEffect hook
對(duì)于useEffect hook,其用途類似于class組件中的生命周期函數(shù),用來(lái)處理在一些特定時(shí)刻需要去做的事情,這種事情常被叫做副作用。在使用useEffect這個(gè)hook時(shí),需要注意的一點(diǎn)就是其不能夠被包含在循環(huán),判斷語(yǔ)句中,否則項(xiàng)目會(huì)出現(xiàn)報(bào)錯(cuò),這也是hook的一種設(shè)置機(jī)制
- 副作用的劃分:
- 不需要清除的: 在React更新DOM之后運(yùn)行一些額外的代碼:如:發(fā)送網(wǎng)絡(luò)請(qǐng)求,手動(dòng)變更DOM,記錄日志等
- 需要清除的:當(dāng)使用外部數(shù)據(jù)源時(shí),需要去清除數(shù)據(jù),如:定時(shí)器,需要我們?cè)诮Y(jié)束的時(shí)候去清除
- 渲染時(shí)機(jī):在使用useEffect這個(gè)hook時(shí),需要注意的就是其渲染的時(shí)機(jī),默認(rèn)情況下會(huì)在第一次渲染和每一次更新時(shí)去執(zhí)行。對(duì)于如何去控制這個(gè)渲染時(shí)機(jī),在下面的一個(gè)部分會(huì)有詳細(xì)的介紹
- 作用:告訴組件在渲染之后執(zhí)行某些操作
- useEffect放在組件內(nèi)部調(diào)用的原因:可以在effect中直接訪問state中的變量
- effect返回函數(shù):effect可選的清除機(jī)制,每個(gè)effect都可以返回一個(gè)清除函數(shù)
- 接收內(nèi)容:一個(gè)包含命令式、并且可能有副作用代碼的函數(shù)
- 清除effect:實(shí)現(xiàn)方式,effect函數(shù)需要返回一個(gè)清除函數(shù)
- effect執(zhí)行時(shí)機(jī):在瀏覽器完成布局和繪制之后,傳給useEffect的函數(shù)會(huì)延遲調(diào)用,因此不應(yīng)該在函數(shù)中執(zhí)行足賽瀏覽器更新屏幕的操作。
- 默認(rèn)條件執(zhí)行:會(huì)在每輪組件渲染完成后執(zhí)行,因而一旦effect的依賴發(fā)生變化,他就會(huì)被重新創(chuàng)建。要改變其執(zhí)行時(shí)機(jī),需要給useEffect傳遞第二個(gè)參數(shù),只有當(dāng)?shù)诙€(gè)參數(shù)值發(fā)生改變才會(huì)重新創(chuàng)建訂閱。如果要使用這個(gè)優(yōu)化的方式,需要確保數(shù)組包含了所有外部作用域中會(huì)發(fā)發(fā)生變化,且在effect中使用的變量。如果只想運(yùn)行一次effect,可以傳遞一個(gè)空數(shù)組作為第二個(gè)參數(shù)。
對(duì)于useEffect的初步認(rèn)識(shí)只需要了解上面的即可。接下來(lái)就來(lái)介紹一個(gè)官網(wǎng)的實(shí)例,來(lái)說明useEffect
import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // Similar to componentDidMount and componentDidUpdate: useEffect(() => { // Update the document title using the browser API document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
在上面的這段代碼中,就使用到了useEffect這個(gè)hook,在每次count值改變時(shí),就會(huì)在頁(yè)面中去打印“You clicked ${count} times”這段文字,當(dāng)然count肯定對(duì)應(yīng)的就是其所對(duì)應(yīng)的值。
useEffect去取代calss中的生命周期函數(shù)的方式
react中有狀態(tài)組件中,其生命周期函數(shù)的各個(gè)階段
- 在Mounting階段
- constructor()
- static getDerivedStateFromProps()
- render()
- componentDidMount()
- Updating
- static getDerivedStateFormProps
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate()
- UnMouting
- componentWillUnmount()
- componentWillUnmount()
使用hook去代替生命周期函數(shù)的方式
這里就介紹了關(guān)于useEffect這個(gè)hook的使用,有一些生命周期函數(shù)就是通過該hook來(lái)實(shí)現(xiàn)的,這里推薦一篇文章https://blog.logrocket.com/guide-to-react-useeffect-hook/,可以參考下。這里是在參考了一些文章后寫的,具體介紹如下:
constructor: 可以通過useState來(lái)初始化state
componentDidMount(),在hook中需要使用下面的這種方式去取代,在useEffect中傳遞第二個(gè)參數(shù),該參數(shù)為一個(gè)空數(shù)組,只會(huì)去執(zhí)行一次,如下面所示
useEffect(() => { },[])
componentDidUpdate(),有兩種方式去解決
在每次渲染的時(shí)候都去調(diào)用hooks,解決的方式如下面所示
useEffect(() => { })
用一個(gè)特殊變量的去觸發(fā)hook,如下面所示,count指的就是這個(gè)特殊的變量,該hook觸發(fā),只會(huì)是count的值改變時(shí)
useEffect(() => { },[count])
componentWillUnmount(),用hook來(lái)代替,需要去return一個(gè)callback(回調(diào)函數(shù)),如下面的形式所示
useEffect(() => { return () => { //執(zhí)行的為componentWillUnmount } },[])
shouldComponentUpdata(),常使用React.memo來(lái)代替,在默認(rèn)情況下,它將對(duì)props對(duì)象中的復(fù)雜對(duì)象進(jìn)行淺層比較,如果想要去控制比較,可以去提供一個(gè)自定義的比較函數(shù)作為第二個(gè)參數(shù)。代替hook的方式如下所示
import React from 'react' function areEqual(prevProps, nextProps) { /* return true if passing nextProps to render would return the same result as passing prevProps to render, otherwise return false */ } const Weather = ({weather}) => { return (<div> <p>{weather.city}</p> <p>{weather.temperature}</p> {console.log('Render')} </div> ) } export default React.memo(Weather, areEqual)
自定義hook
通常在實(shí)際的項(xiàng)目開發(fā)中少不了使這種自定義的hook,前提是在整個(gè)項(xiàng)目中使用了hook的情況下。通常情況下就是去使用useState,useEffect這種系統(tǒng)已經(jīng)定義好的hook去實(shí)現(xiàn),在調(diào)用時(shí)你就可以直接調(diào)用當(dāng)你自定義好的hook來(lái)實(shí)現(xiàn)你所需要的功能。下面就以自定義useReducer這個(gè)hook為例
import React, { useEffect } from 'react' function useReducer(reducer, initialState) { const [state, setState] = useState(initialState); function dispatch(action) { const nextState = reducer(state, action); setState(nextState); } return [state, dispatch]; } export default useReducer
在上面的這個(gè)實(shí)際例子中,我們封裝了一個(gè)自定義的useReducerhook,我們可以調(diào)用這個(gè)hook去完成與reducer一樣的功能了,在調(diào)用是就需要我們?nèi)魅雰蓚€(gè)參數(shù),一個(gè)就是reducer,另外一個(gè)就是initialState,然后就能夠取得state,以及dispatch方法。注意這里的返回值使用的是一個(gè)數(shù)組,這樣的好處就是我們?cè)讷@取其返回值時(shí),可以采用數(shù)組結(jié)構(gòu)這種方式來(lái)獲取。具體關(guān)于數(shù)組的結(jié)構(gòu)可以去看看es6中的部分,就能夠明白了。那么接下來(lái)就是使用這個(gè)自定義好的useReducer。使用方式如下
import useReducer form '你封裝useRecuer的組件中' function Todos() { const todosReducer = ( state, dispatch) => { if(dispatch.type == "") { //type值為什么時(shí)去執(zhí)行 const newState == "" //執(zhí)行一些操作,去更新state return newState //返回新的neState } } const [todos, dispatch] = useReducer(todosReducer, []); function handleAddClick(text) { dispatch({ type: 'add', text }); } return ( <div></div> ) }
這里并沒有把實(shí)際的使用情況給寫完,剩余的可以自己去補(bǔ)充,其使用方式就和redux的使用方式相同。這就是整個(gè)自定義hook以及去使用的過程,在實(shí)際的開發(fā)中可以去體驗(yàn)體驗(yàn)。
額外的hook
useReducer,能給那些會(huì)出發(fā)深更新的組件做性能優(yōu)化,因?yàn)榭梢韵蜃咏M件去傳遞dispatch而不是回調(diào)
useReducer這個(gè)hook的封裝,整個(gè)封裝的方法如下:
//reducer hook封裝 import { useState } from 'react'; export default useReducer function(reducer, initialState) { const [state, setState] = useState(initialState); function dispatch(action){ const nextState = reducer(state, action); return setState(nextState); } return [state, dispatch] } //實(shí)際例子使用 import useReducer from ''; const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; default: throw new Error(); } } return ( <div> Count: {state.count} <button onClick={() => dispatch({type: 'devrement'})}>-</button> <button onClick={() => dispatch({type: 'increment'})}>+</button> </div> )
useReducer的惰性初始化,可以選擇惰性地創(chuàng)建初始化state。因此需要設(shè)置一個(gè)初始化函數(shù)作為useReducer的第三個(gè)參數(shù)傳入,這樣初始化state將設(shè)置為init(initialArg),如下所示,就是一個(gè)實(shí)際的案例在useReducer中去傳遞第三個(gè)參數(shù)
function init(initialCount) { return {count: initialCount}; } function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; case 'reset': return init(action.payload); default: throw new Error(); } } function Counter({initialCount}) { const [state, dispatch] = useReducer(reducer, initialCount, init); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'reset', payload: initialCount})}> Reset </button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> <button onClick={() => dispatch({type: 'increment'})}>+</button> </> ); }
注意:如果reducer hook的返回值與當(dāng)前state相同,react將跳過子組件的渲染及副作用的執(zhí)行
useCallback
返回值:返回一個(gè)memoized回調(diào)函數(shù),該回調(diào)函數(shù)僅在某給依賴項(xiàng)改變時(shí)才會(huì)更新。
含義:把內(nèi)聯(lián)回調(diào)函數(shù)及其依賴項(xiàng)數(shù)組作為參數(shù)傳入useCallback,它將返回該回調(diào)函數(shù)傳遞給經(jīng)過優(yōu)化的并使用引用相等性去避免非必要渲染
useCallBack(fn, deps)相當(dāng)與useMemo(() => fn,deps)
useMemo
使用方式:const memoziedValue = useMemo(() => computeExpensiveValue(a,b), [a, b])
返回值:返回一個(gè)memoized值,把創(chuàng)建函數(shù)和依賴項(xiàng)數(shù)組作為參數(shù)傳入useMemo,僅在某個(gè)依賴項(xiàng)改變時(shí)才重新計(jì)算memoized值。
好處:這種優(yōu)化有助于避免在每次渲染時(shí)都進(jìn)行高開銷的計(jì)算
渲染方式:傳入useMemo的函數(shù)會(huì)在渲染期間執(zhí)行,不要在這個(gè)函數(shù)內(nèi)部執(zhí)行與渲染無(wú)關(guān)的操作,如屬于useEffect中的副作用。如果沒有,那么新的值將會(huì)在每次渲染時(shí)被重新渲染
注意:依賴項(xiàng)數(shù)組不會(huì)作為參數(shù)傳遞給函數(shù),概述來(lái)說,就是每一個(gè)出現(xiàn)在函數(shù)中的參數(shù)也應(yīng)該出現(xiàn)在依賴項(xiàng)的數(shù)組中
useRef
使用方式: const refContainer = useref(initialValue);
返回值:返回一個(gè)可ref對(duì)象,其.current屬性被初始化為傳入的參數(shù)(initialValue)。這返回的的對(duì)象將在組件的整個(gè)生命周期中持續(xù)
含義: useRef就像是一個(gè)盒子可以將.current中得可變屬性給保存起來(lái)
ref與useRef的區(qū)別在于,后者是創(chuàng)建的了一個(gè)普通的js對(duì)象,useRef和自建一個(gè){current: …。}對(duì)象的唯一區(qū)別是,useRef會(huì)在每次渲染時(shí),返回同一個(gè)ref對(duì)象
useImperativeHandle
作用:可以在使用ref時(shí)自定義暴露給賦組件的實(shí)例值,使用的形式如下:
useImperativeHandle(ref, createHandle, [deps])
useLayoutEffect
更新時(shí)機(jī):在瀏覽器執(zhí)行下一次繪制前去執(zhí)行
與useEffect相同,會(huì)在所有的DOM變更之后同步調(diào)用effect
useDebugValue
作用:在react devTools中常被用于去當(dāng)作展示標(biāo)簽,作為客戶端的鉤子
hooks中的性能優(yōu)化
在hook中,其性能優(yōu)化的點(diǎn)很多,這個(gè)可以在一些https://react.docschina.org/docs/hooks-faq.html#performance-optimizations去學(xué)習(xí),下面是我看的一部分。
如何在更新時(shí)去跳過effect,可以采用條件式方式,即在useEffect中去傳遞第二個(gè)參數(shù)
由于某些原因,無(wú)法將一個(gè)函數(shù)移動(dòng)到effect內(nèi)部時(shí),可采用下面方式
- 嘗試將函數(shù)移動(dòng)到當(dāng)前組件的外部
- 如果所調(diào)用對(duì)策方法是一個(gè)純計(jì)算等,此時(shí)可以在effect外面去寫這個(gè)函數(shù)
- 如果要增加一個(gè)函數(shù)去依賴項(xiàng),那么要明確使用useCallback外部的hook,如下面的例子所示
function ProductPage({ productId }) { // Wrap with useCallback to avoid change on every render const fetchProduct = useCallback(() => { // ... Does something with productId ... }, [productId]); // All useCallback dependencies are specified return <ProductDetails fetchProduct={fetchProduct} />; } function ProductDetails({ fetchProduct }) { useEffect(() => { fetchProduct(); }, [fetchProduct]); // All useEffect dependencies are specified // ... }
實(shí)現(xiàn)shouldComponentUpdate的方式
const Button = React.memo((props) => { // your component });
如上面所示,這種實(shí)現(xiàn)方式并不是使用了hooks,它相當(dāng)于純組件,但是僅僅能夠比較的是props??梢匀ピ黾拥诙€(gè)參數(shù),采用一種函數(shù)的方式去拿到新老的props,如果結(jié)果返回true,就跳過更新階段
記住計(jì)算結(jié)果的方式
使用useMemo這個(gè)hook去記住之前的計(jì)算結(jié)果,從而在多個(gè)渲染之中緩存計(jì)算
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
上面的代碼會(huì)調(diào)用computeExpensiveValue(a,b)這個(gè)函數(shù),但是它們依賴的a,b沒有改變,那么useMemo在直接去返回上一次結(jié)果的值
結(jié)語(yǔ)
對(duì)于hook的學(xué)習(xí)大概就如上面所說,對(duì)于hook其中的內(nèi)容還很多所以對(duì)于hook的學(xué)習(xí)最好是去官網(wǎng)看看,鏈接如下https://react.docschina.org/docs/hooks-intro.html在官網(wǎng)中介紹的更加詳細(xì),這里的中文文檔和英文文檔內(nèi)容都一樣,不過對(duì)于英文好的同學(xué)建議看看英文版本。
到此這篇關(guān)于react中hook的介紹以及使用的文章就介紹到這了,更多相關(guān)react中hook介紹及使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react?native?reanimated實(shí)現(xiàn)動(dòng)畫示例詳解
這篇文章主要為大家介紹了react?native?reanimated實(shí)現(xiàn)動(dòng)畫示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03關(guān)于react hook useState連續(xù)更新對(duì)象的問題
這篇文章主要介紹了關(guān)于react hook useState連續(xù)更新對(duì)象的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03react ant protable自定義實(shí)現(xiàn)搜索下拉框
這篇文章主要介紹了react ant protable自定義實(shí)現(xiàn)搜索下拉框,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06React Fiber結(jié)構(gòu)的創(chuàng)建步驟
這篇文章主要介紹了React Fiber結(jié)構(gòu)的創(chuàng)建步驟,幫助大家更好的理解和學(xué)習(xí)使用React,感興趣的朋友可以了解下2021-04-04