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,頁面ui就會(huì)自動(dòng)更新,及實(shí)現(xiàn)了響應(yīng)式。
類組件中使用state:
函數(shù)組件需要借助useState
:
用法:
const [stateName, setStateName] = React.useState(stateValue);
以下代碼實(shí)現(xiàn)了一秒后頁面上的文字變化:
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ù)返回的值,所以在頁面上直接使用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動(dòng)畫實(shí)現(xiàn)方案Framer Motion讓頁面自己動(dòng)起來
這篇文章主要為大家介紹了React動(dòng)畫實(shí)現(xiàn)方案Framer Motion讓頁面自己動(dòng)起來,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10React學(xué)習(xí)筆記之高階組件應(yīng)用
這篇文章主要介紹了React 高階組件應(yīng)用,詳細(xì)的介紹了什么是React高階組件和具體使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06react性能優(yōu)化useMemo與useCallback使用對(duì)比詳解
這篇文章主要為大家介紹了react性能優(yōu)化useMemo與useCallback使用對(duì)比詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08webpack+react+antd腳手架優(yōu)化的方法
本篇文章主要介紹了webpack+react+antd腳手架優(yōu)化的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-04-04react滾動(dòng)加載useInfiniteScroll?詳解
使用useInfiniteScroll?hook可以處理檢測(cè)用戶何時(shí)滾動(dòng)到頁面底部的邏輯,并觸發(fā)回調(diào)函數(shù)以加載更多數(shù)據(jù),它還提供了一種簡(jiǎn)單的方法來管理加載和錯(cuò)誤消息的狀態(tài),今天通過實(shí)例代碼介紹下react滾動(dòng)加載useInfiniteScroll?相關(guān)知識(shí),感興趣的朋友跟隨小編一起看看吧2023-09-09React18+TS通用后臺(tái)管理系統(tǒng)解決方案落地實(shí)戰(zhàn)示例
這篇文章主要為大家介紹了React18+TS通用后臺(tái)管理系統(tǒng)解決方案落地實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08