1、useState:讓函數(shù)式組件擁有狀態(tài)
用法示例:
import { useState } from 'react'
const Test = () => {
const [count, setCount] = useState(0);
return (
<>
<h1>點(diǎn)擊了{count}次</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
</>
);
}
export default Test
PS:class組件中this.setState更新是state是合并, useState中setState是替換。例如:
import { useState } from 'react'
const Test = () => {
const [counts, setCounts] = useState({
num1: 0,
num2: 0
});
return (
<>
<h1>num1:{counts.num1}</h1>
<h1>num2:{counts.num2}</h1>
<button onClick={() => setCounts({ num1: counts.num1 + 1})}>num1+1</button>
<button onClick={() => setCounts({ num2: counts.num2 + 1})}>num2+1</button>
</>
);
}
export default Test
可以看到useState中setState是替換豪直,不會(huì)合并袱结,正確更新:
import { useState } from 'react'
const Test = () => {
const [counts, setCounts] = useState({
num1: 0,
num2: 0
});
return (
<>
<h1>num1:{counts.num1}</h1>
<h1>num2:{counts.num2}</h1>
<button onClick={() => setCounts({ ...counts, num1: counts.num1 + 1})}>num1+1</button>
<button onClick={() => setCounts({ ...counts, num2: counts.num2 + 1})}>num2+1</button>
</>
);
}
export default Test
2芝发、useEffect:副作用霞掺,取代生命周期
用法示例秘蛇,在class組件中如果需要在組件掛載后和數(shù)據(jù)更新后做同一件事备燃,我們會(huì)這樣做:
componentDidMount() {
}
componentDidUpdate() {
}
可以看出來碉克,如果邏輯復(fù)雜后,代碼看起來不優(yōu)雅并齐,且容易造成邏輯混亂漏麦,而使用useEffect:
useEffect(() => {
});
此刻已經(jīng)看到了useEffect的基本用法客税,除此之外,他還可以綁定觸發(fā)更新的依賴狀態(tài)撕贞,默認(rèn)是狀態(tài)中任何數(shù)據(jù)發(fā)生變化副作用都會(huì)執(zhí)行更耻,如:
import { useState, useEffect } from 'react'
const Test = () => {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
useEffect(() => {
console.log('useEffect觸發(fā)了')
});
return (
<>
<h1>count1:{count1}</h1>
<h1>count2:{count2}</h1>
<button onClick={() => setCount1(count1 + 1)}>count1+1</button>
<button onClick={() => setCount2(count2 + 1)}>count2+1</button>
</>
);
}
export default Test
將上述代碼useEffect第二個(gè)參數(shù)傳入需要綁定的狀態(tài),可綁定多個(gè):
useEffect(() => {
console.log('useEffect觸發(fā)了')
}, [count1]);
可以看到捏膨,只有綁定的count1發(fā)生變化才會(huì)觸發(fā)秧均,如果傳空數(shù)組則任何狀態(tài)發(fā)生變化都不會(huì)觸發(fā),此時(shí)useEffect的作用就類似class組件中的componentDidMount号涯,所以發(fā)送請(qǐng)求通常也會(huì)在此執(zhí)行目胡。
清理副作用 在上面的操作中都不用清理的副作用,然而诚隙,有些副作用是需要去清理的讶隐,不清理會(huì)造成異常甚至內(nèi)存泄漏,比如開啟定時(shí)器久又,如果不清理巫延,則會(huì)多次開啟,從上面可以看到useEffect的第一個(gè)參數(shù)是一個(gè)回調(diào)函數(shù)地消,可以在回調(diào)函數(shù)中再返回一個(gè)函數(shù)炉峰,該函數(shù)可以在狀態(tài)更新后第一個(gè)回調(diào)函數(shù)執(zhí)行之前調(diào)用,具體實(shí)現(xiàn):
seEffect(() => {
return () => {
}
});
3脉执、useContext:跨組件共享數(shù)據(jù)
React.createContext();創(chuàng)建一個(gè)TestContext對(duì)象
TestContext.Provider包裹子組件
數(shù)據(jù)放在<TestContext.Provider value={value}>的value中
子組件中通過useContext(TestContext)獲取值
import React, { useContext, useState } from 'react';
const TestContext = React.createContext();
const Parent = () => {
const [value, setValue] = useState(0);
return (
<div>
{(() => console.log("Parent-render"))()}
<button onClick={() => setValue(value + 1)}>value + 1</button>
<TestContext.Provider value={value}>
<Child1 />
<Child2 />
</TestContext.Provider>
</div>
);
}
const Child1 = () => {
const value = useContext(TestContext);
return (
<div>
{(() => console.log('Child1-render'))()}
<h3>Child1-value: {value}</h3>
</div>
);
}
const Child2 = () => {
return (
<div>
{(() => console.log('Child2-render'))()}
<h3>Child2</h3>
</div>
);
}
export default Parent
至此數(shù)據(jù)實(shí)現(xiàn)共享了疼阔,但是可以看到在TestContext中的共享數(shù)據(jù)只要發(fā)生變化,子組件都會(huì)重新渲染半夷,Child2并沒有綁定數(shù)據(jù)婆廊,不希望他做無意義的渲染,可以使用React.memo解決巫橄,實(shí)現(xiàn):
const Child2 = React.memo(() => {
return (
<div>
{(() => console.log('Child2-render'))()}
<h3>Child2</h3>
</div>
);
});
4淘邻、useCallback:性能優(yōu)化
const handleClick = useCallback(()=> {
}, [value]);
useCallback返回的是一個(gè) memoized(緩存)函數(shù),在依賴不變的情況下,多次定義的時(shí)候,返回的值是相同的湘换,他的實(shí)現(xiàn)原理是當(dāng)使用一組參數(shù)初次調(diào)用函數(shù)時(shí)宾舅,會(huì)緩存參數(shù)和計(jì)算結(jié)果,當(dāng)再次使用相同的參數(shù)調(diào)用該函數(shù)時(shí)彩倚,會(huì)直接返回相應(yīng)的緩存結(jié)果筹我。
優(yōu)化性能例子:
import React, { useState, useCallback, memo } from 'react';
const Parent = () => {
const [value1, setValue1] = useState(0);
const [value2, setValue2] = useState(0);
const handleClick1 = useCallback(()=> {
setValue1(value1 + 1);
}, [value1]);
const handleClick2 = useCallback(()=> {
setValue2(value2 + 1);
}, [value2]);
return (
<>
{(() => console.log("Parent-render"))()}
<h3>{value1}</h3>
<h3>{value2}</h3>
<Child1 handleClick1={handleClick1} />
<Child2 handleClick2={handleClick2} />
</>
);
}
const Child1 = memo(props => {
return (
<div>
{(() => console.log("Child1-render"))()}
<button onClick={() => props.handleClick1()}>value1 + 1</button>
</div>
);
});
const Child2 = memo(props => {
return (
<div>
{(() => console.log("Child2-render"))()}
<button onClick={() => props.handleClick2()}>value2 + 1</button>
</div>
);
});
export default Parent
useCallback返回的是一個(gè)memoized回調(diào)函數(shù),僅在其中綁定的一個(gè)依賴項(xiàng)變化后才更改可防止不必要的渲染帆离,在跨組件共享數(shù)據(jù)中舉例的事件是在父組件中點(diǎn)擊觸發(fā)蔬蕊,而現(xiàn)在是使用狀態(tài)提升,在父組件中傳遞方法供子組件調(diào)用哥谷,每次render時(shí)函數(shù)也會(huì)變化袁串,導(dǎo)致子組件重新渲染概而,上面例子useCallback將函數(shù)進(jìn)行包裹,依賴值未發(fā)生變化時(shí)會(huì)返回緩存的函數(shù)囱修,配合React.memo即可優(yōu)化無意義的渲染赎瑰。
5、useMemo:性能優(yōu)化
語法:
useMemo(() => {
},[value]);
先看一個(gè)例子:
import React, { useState } from 'react'
const Test = ()=> {
const [value, setValue] = useState(0);
const [count, setCount] = useState(1);
const getDoubleCount = () => {
console.log('getDoubleCount進(jìn)行計(jì)算了');
return count * 2;
};
return (
<div>
<h2>value: {value}</h2>
<h2>doubleCount: {getDoubleCount()}</h2>
<button onClick={() => setValue(value + 1)}>value+1</button>
</div>
)
}
export default Test
可以看到getDoubleCount依賴的是count破镰,但value發(fā)生變化它也重新進(jìn)行了計(jì)算渲染餐曼,現(xiàn)在只需要將getDoubleCount使用useMemo進(jìn)行包裹,如下:
import React, { useState, useMemo } from 'react'
const Test = ()=> {
const [value, setValue] = useState(0);
const [count, setCount] = useState(1);
const getDoubleCount = useMemo(() => {
console.log('getDoubleCount進(jìn)行計(jì)算了');
return count * 2;
},[count]);
return (
<div>
<h2>value: {value}</h2>
<h2>doubleCount: {getDoubleCount}</h2>
<button onClick={() => setValue(value + 1)}>value+1</button>
</div>
)
}
export default Test
現(xiàn)在getDoubleCount只有依賴的count發(fā)生變化時(shí)才會(huì)重新計(jì)算渲染鲜漩。
useMemo和useCallback的共同點(diǎn):
接收的參數(shù)都是一樣的源譬,第一個(gè)是回調(diào)函數(shù),第二個(gè)是依賴的數(shù)據(jù) 它們都是當(dāng)依賴的數(shù)據(jù)發(fā)生變化時(shí)才會(huì)重新計(jì)算結(jié)果孕似,起到了緩存作用
useMemo和useCallback的區(qū)別:
useMemo計(jì)算結(jié)果是return回來的值踩娘,通常用于緩存計(jì)算結(jié)果的值 useCallback計(jì)算結(jié)果是一個(gè)函數(shù),通常用于緩存函數(shù)
6喉祭、useRef
用法:例如要實(shí)現(xiàn)點(diǎn)擊button按鈕使input輸入框獲得焦點(diǎn):
import React, { useRef } from 'react';
const Test = () => {
const inputEl = useRef();
return (
<>
<input ref={inputEl} />
<button onClick={() => inputEl.current.focus()}>focus</button>
</>
);
}
export default Test
這樣看起來非常像React.createRef()养渴,將上面代碼中的useRef()改成React.createRef()也能實(shí)現(xiàn)同樣的效果,那為什么要設(shè)計(jì)一個(gè)新的hook泛烙?難道只是為了加上use理卑,統(tǒng)一hook規(guī)范? 事實(shí)上蔽氨,它們確實(shí)不一樣藐唠。
官網(wǎng)的說明如下:
useRef returns a mutable ref object whose .current property is initialized to the passed
argument (initialValue). The returned object will persist for the full lifetime of the component.
簡單來說,useRef就像一個(gè)儲(chǔ)物箱鹉究,你可以隨意存放任何東西宇立,再次渲染時(shí)它會(huì)去儲(chǔ)物箱找,createRef每次渲染都會(huì)返回一個(gè)新的引用自赔,而useRef每次都會(huì)返回相同的引用妈嘹。
摘自:https://www.cnblogs.com/hymenhan/archive/2021/04/28/14711516.html
本文使用 文章同步助手 同步