React組件學(xué)習(xí)之Hooks使用
一、前言
react組件分為類(class)組件和函數(shù)(function)組件。
class 組件是通過繼承模版類(Component、PureComponent)的方式開發(fā)新組件的,繼承是 class 本身的特性,它支持設(shè)置 state,會(huì)在 state 改變后重新渲染,可以重寫一些父類的方法,這些方法會(huì)在 React 組件渲染的不同階段調(diào)用,叫做生命周期函數(shù)。
function 組件不能做繼承,因?yàn)?function 本來就沒這個(gè)特性,并且函數(shù)式組件也沒有生命周期,所以react提供了一些 api 供函數(shù)來彌補(bǔ)函數(shù)組件的缺陷,這些 api 會(huì)在內(nèi)部的一個(gè)數(shù)據(jù)結(jié)構(gòu)上掛載一些函數(shù)和值,并執(zhí)行相應(yīng)的邏輯,通過這種方式實(shí)現(xiàn)了 state 和類似 class 組件的生命周期函數(shù)的功能,這種 api 就叫做 hooks。
二、React Hooks
其實(shí)我們已經(jīng)使用過一些hooks了,例如useState來實(shí)現(xiàn)響應(yīng)式的數(shù)據(jù)功能,使用useEffect實(shí)現(xiàn)類組件才擁有的組件生命周期,但是都沒有詳細(xì)地研究其參數(shù)和使用。
2.1 useState
state保存著當(dāng)前組件的狀態(tài),當(dāng)我們改變state,頁(yè)面ui就會(huì)自動(dòng)更新,及實(shí)現(xiàn)了響應(yīng)式。
類組件中使用state:


函數(shù)組件需要借助useState:
用法:
const [stateName, setStateName] = React.useState(stateValue);
以下代碼實(shí)現(xiàn)了一秒后頁(yè)面上的文字變化:
function MyComponent() {
const [msg, setMsg] = React.useState("Hello React!");
setTimeout(() => setMsg("Hello Hooks!"), 1000);
return <p>{msg}</p>;
}
ReactDOM.render(<MyComponent />, document.getElementById("app"));效果:

2.2 useEffect
React hooks也提供了 api ,用于彌補(bǔ)函數(shù)組件沒有生命周期的缺陷。
用法:
useEffect(()=>{
return destory
},dep)
useEffect 第一個(gè)參數(shù) 是一個(gè)函數(shù), 返回的 destory , destory 作為下一次callback執(zhí)行之前調(diào)用,用于清除上一次 回調(diào)函數(shù)產(chǎn)生的副作用。
第二個(gè)參數(shù)作為依賴項(xiàng),是一個(gè)數(shù)組,可以有多個(gè)依賴項(xiàng),依賴項(xiàng)改變,執(zhí)行上一次回調(diào)函數(shù)返回的 destory ,和執(zhí)行新的 effect 第一個(gè)參數(shù)。
對(duì)于 useEffect 執(zhí)行, React 處理邏輯是采用異步調(diào)用 ,對(duì)于每一個(gè) effect 的 callback, React 會(huì)向 setTimeout回調(diào)函數(shù)一樣,放入任務(wù)隊(duì)列,等到主線程任務(wù)完成,DOM 更新,js 執(zhí)行完成,視圖繪制完畢,才執(zhí)行。所以 effect 回調(diào)函數(shù)不會(huì)阻塞瀏覽器繪制視圖。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>useEffect</title>
<script src="https://cdn.staticfile.org/react/16.8.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.8.0/umd/react-dom.development.js"></script>
<!-- 生產(chǎn)環(huán)境中不建議使用 -->
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
<div id="app"></div>
<script type="text/babel">
const Clock = (props) => {
const [n, setN] = React.useState(0);
function addNum() {
setN(n + 1);
}
React.useEffect(() => {
console.log(n);
});
return (
<div>
<p>現(xiàn)在的n是 {n} .</p>
<button onClick={addNum}>n+1</button>
</div>
);
};
ReactDOM.render(<Clock />, document.getElementById("app"));
</script>
</body>
</html>
- 組合 componentDidMount componentDidUpdate
當(dāng)useEffect沒有第二個(gè)參數(shù)時(shí),組件的初始化和更新都會(huì)執(zhí)行。
useEffect(() => {
//doSomething
}, [])
- componentDidMount
useEffect的第二個(gè)參數(shù)為一個(gè)空數(shù)組,初始化調(diào)用一次之后不再執(zhí)行,相當(dāng)于componentDidMount。
useEffect(() => {
//doSomething
}, [])
- 組合 componentDidMount componentWillUnmount
useEffect返回一個(gè)函數(shù),這個(gè)函數(shù)會(huì)在組件卸載時(shí)執(zhí)行。
useEffect(() => {
return () => {
...
};
}, []);
2.3 useMemo
它返回的是一個(gè) memoized 值,僅會(huì)在某個(gè)依賴項(xiàng)改變時(shí)才重新計(jì)算 memoized 值。這種優(yōu)化有助于避免在每次渲染時(shí)都進(jìn)行高開銷的計(jì)算。我們可以拿 vue 里面的 計(jì)算屬性computed和它做一個(gè)對(duì)比,都是返回基于依賴重新計(jì)算的值。
const cacheSomething = useMemo(create,deps)
① create:第一個(gè)參數(shù)為一個(gè)函數(shù),函數(shù)的返回值作為緩存值。
② deps: 第二個(gè)參數(shù)為一個(gè)數(shù)組,存放當(dāng)前 useMemo 的依賴項(xiàng),在函數(shù)組件下一次執(zhí)行的時(shí)候,會(huì)對(duì)比 deps 依賴項(xiàng)里面的狀態(tài),是否有改變,如果有改變重新執(zhí)行 create ,得到新的緩存值。
③ acheSomething:返回值,執(zhí)行 create 的返回值。如果 deps 中有依賴項(xiàng)改變,返回的重新執(zhí)行 create 產(chǎn)生的值,否則取上一次緩存值。
function MyComponent() {
const [n, setN] = React.useState(0);
const [t, setT] = React.useState(0);
const getT = () => {
console.log("調(diào)用getT獲取T!");
return t;
};
return (
<div>
<p>n: {n}</p>
<p>t: {getT}</p>
<button onClick={() => setN(n + 1)}>n + 1</button>
<br />
<button onClick={() => setT(t + 1)}>t + 1</button>
</div>
);
}
ReactDOM.render(<MyComponent />, document.getElementById("app"));此時(shí)無論改變n還是t,都會(huì)導(dǎo)致getT函數(shù)被調(diào)用,但是事實(shí)上,在n改變時(shí),并不需要重新調(diào)用一遍getT函數(shù)。

我們使用useMemo來實(shí)現(xiàn)僅t改變,getT才重新運(yùn)行。
function MyComponent() {
const [n, setN] = React.useState(0);
const [t, setT] = React.useState(0);
const getT = React.useMemo(() => {
console.log("調(diào)用getT獲取T!");
return t;
}, [t]);
return (
<div>
<p>n: {n}</p>
<p>t: {getT}</p>
<button onClick={() => setN(n + 1)}>n + 1</button>
<br />
<button onClick={() => setT(t + 1)}>t + 1</button>
</div>
);注意,此時(shí)getT不在是一個(gè)函數(shù),而是useMemo回調(diào)函數(shù)返回的值,所以在頁(yè)面上直接使用getT即可。

2.4 useCallback
useMemo 和 useCallback 接收的參數(shù)都是一樣,都是在其依賴項(xiàng)發(fā)生變化后才執(zhí)行,都是返回緩存的值。
區(qū)別在于 useMemo 返回的是函數(shù)運(yùn)行的結(jié)果,useCallback 返回的是函數(shù),這個(gè)回調(diào)函數(shù)是經(jīng)過處理后的也就是說父組件傳遞一個(gè)函數(shù)給子組件的時(shí)候。
由于是無狀態(tài)組件每一次都會(huì)重新生成新的 props 函數(shù),這樣就使得每一次傳遞給子組件的函數(shù)都發(fā)生了變化,這時(shí)候就會(huì)觸發(fā)子組件的更新,這些更新是沒有必要的,此時(shí)我們就可以通過 usecallback 來處理此函數(shù),然后作為 props 傳遞給子組件。
我們依然利用useMemo的例子:

可以看到,此時(shí)getT是一個(gè)函數(shù),并不是一個(gè)值,這也是useMemo和useCallback的區(qū)別,即返回結(jié)果不同。
2.5 useContext
可以使用 useContext ,來獲取父級(jí)組件傳遞過來的 context 值(上下文),這個(gè)當(dāng)前值就是最近的父級(jí)組件 Provider 設(shè)置的 value 值,useContext 參數(shù)一般是由 createContext 方式創(chuàng)建的 ,也可以父級(jí)上下文 context 傳遞的 ( 參數(shù)為 context )。
創(chuàng)建context:
const context = createContext();
使用context:
const contextValue = useContext(context)
const Context = React.createContext();
const ChildComponent01 = () => {
const c = React.useContext(Context);
return (
<div>
<p>/* 用useContext方式 */</p>
My name is {c.name}, I'm {c.age}.
</div>
);
};
const ChildComponent02 = () => {
return (
<Context.Consumer>
{(value) => (
<div>
<p>/* 用Context.Consumer 方式 */</p>
My name is {value.name}, I'm {value.age}.
</div>
)}
</Context.Consumer>
);
};
const MyComponent = () => {
return (
<div>
<Context.Provider value={{ name: "yancy", age: 20 }}>
<ChildComponent01 />
<ChildComponent02 />
</Context.Provider>
</div>
);
}
ReactDOM.render(<MyComponent />, document.getElementById("app"));使用useContext可以避免使用props進(jìn)行傳參造成數(shù)據(jù)傳遞十分繁瑣和困難的問題。
2.6 useRef
在 React 數(shù)據(jù)流中,props 是父組件和子組件交互的唯一方式。要修改一個(gè)子組件,必須使用新的 props 去重新渲染它。而 refs 提供了另一種方式,允許我們?cè)?React 典型數(shù)據(jù)流之外,去操作 DOM 元素和類組件的實(shí)例。
useRef 返回一個(gè)對(duì)象,返回的ref對(duì)象在組件的整個(gè)生命周期保持不變。
最常用的ref是兩種對(duì)象
用法1: 引入DOM(或者組件,組件必須是類組件)元素
用飯2: 保存一個(gè)數(shù)據(jù),這個(gè)數(shù)據(jù)在組件的整個(gè)生命周期中可以保存
基于組件的框架(react、vue)是不推薦直接操作dom元素的,例如使用document.getElementById(“”)來獲取元素,react提供了useRef來使我們能夠獲取綁定的dom元素。
const MyComponent = () => {
const titleRef = React.useRef();
const inputRef = React.useRef();
function changeDOM() {
titleRef.current.innerHTML = "useRef";
inputRef.current.focus();
}
return (
<div>
<h2 ref={titleRef}> Hello World </h2>
<input ref={inputRef} type="text" />
<button onClick={(e) => changeDOM()}>修改DOM</button>
</div>
);
};
ReactDOM.render(<MyComponent />, document.getElementById("app"));
還可以利用useRef配合useEffect來獲取上一次的state:
const MyComponent = () => {
const [count, setCount] = React.useState(0);
const numRef = React.useRef(count);
React.useEffect(() => {
numRef.current = count;
}, [count]);
return (
<div>
<h2>count 上一次的值: {numRef.current}</h2>
<h2>count 這一次的值: {count}</h2>
<button onClick={(e) => setCount(count + 1)}>count + 1</button>
</div>
);
};
ReactDOM.render(<MyComponent />, document.getElementById("app"));
三、總結(jié)
在 react 的 class組件寫法中,隨處可見各種各樣的 .bind(this)。(甚至官方文檔里也有專門的章節(jié)描述了“為什么綁定是必要的?”這一問題)
而在函數(shù)組件中通過使用hooks,可以完美地代替類組件,并且?guī)缀醪挥藐P(guān)心this的指向問題。
Vue3.2的組合式api也引入了hooks,可以看到hooks是前端框架發(fā)展的趨勢(shì),是一個(gè)組件的靈魂所在。
到此這篇關(guān)于React組件學(xué)習(xí)之Hooks使用的文章就介紹到這了,更多相關(guān)React Hooks內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
為react組件庫(kù)添加typescript類型提示的方法
這篇文章主要介紹了為react組件庫(kù)添加typescript類型提示,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
React動(dòng)畫實(shí)現(xiàn)方案Framer Motion讓頁(yè)面自己動(dòng)起來
這篇文章主要為大家介紹了React動(dòng)畫實(shí)現(xiàn)方案Framer Motion讓頁(yè)面自己動(dòng)起來,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
React學(xué)習(xí)筆記之高階組件應(yīng)用
這篇文章主要介紹了React 高階組件應(yīng)用,詳細(xì)的介紹了什么是React高階組件和具體使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06
react性能優(yōu)化useMemo與useCallback使用對(duì)比詳解
這篇文章主要為大家介紹了react性能優(yōu)化useMemo與useCallback使用對(duì)比詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
webpack+react+antd腳手架優(yōu)化的方法
本篇文章主要介紹了webpack+react+antd腳手架優(yōu)化的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-04-04
react滾動(dòng)加載useInfiniteScroll?詳解
使用useInfiniteScroll?hook可以處理檢測(cè)用戶何時(shí)滾動(dòng)到頁(yè)面底部的邏輯,并觸發(fā)回調(diào)函數(shù)以加載更多數(shù)據(jù),它還提供了一種簡(jiǎn)單的方法來管理加載和錯(cuò)誤消息的狀態(tài),今天通過實(shí)例代碼介紹下react滾動(dòng)加載useInfiniteScroll?相關(guān)知識(shí),感興趣的朋友跟隨小編一起看看吧2023-09-09
React18+TS通用后臺(tái)管理系統(tǒng)解決方案落地實(shí)戰(zhàn)示例
這篇文章主要為大家介紹了React18+TS通用后臺(tái)管理系統(tǒng)解決方案落地實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08

