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 useImperativeHandle處理組件狀態(tài)和生命周期用法詳解
React Hooks 為我們提供了一種全新的方式來處理組件的狀態(tài)和生命周期,useImperativeHandle是一個相對較少被提及的Hook,但在某些場景下,它是非常有用的,本文將深討useImperativeHandle的用法,并通過實例來加深理解2023-09-09
React?Hooks使用startTransition與useTransition教程示例
這篇文章主要為大家介紹了React?Hooks使用startTransition與useTransition教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
React 模塊聯(lián)邦多模塊項目實戰(zhàn)詳解
這篇文章主要介紹了React 模塊聯(lián)邦多模塊項目實戰(zhàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10

