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