1、useState:讓函式式組件擁有狀態
用法示例:
// 計數器
import { useState } from 'react'
const Test = () => {
const [count, setCount] = useState(0);
return (
<>
<h1>點擊了{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是替換,不會合并,正確更新:
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組件中如果需要在組件掛載后和資料更新后做同一件事,我們會這樣做:
componentDidMount() {
// 做一些事
}
componentDidUpdate() {
// 做一些事
}
可以看出來,如果邏輯復雜后,代碼看起來不優雅,且容易造成邏輯混亂,而使用useEffect:
useEffect(() => {
// 做一些事
});
此刻已經看到了useEffect的基本用法,除此之外,他還可以系結觸發更新的依賴狀態,默認是狀態中任何資料發生變化副作用都會執行,如:
import { useState, useEffect } from 'react'
const Test = () => {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
useEffect(() => {
console.log('useEffect觸發了')
});
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第二個引數傳入需要系結的狀態,可系結多個:
// 語法:useEffect(回呼函式,[依賴值])
useEffect(() => {
console.log('useEffect觸發了')
}, [count1]);

可以看到,只有系結的count1發生變化才會觸發,如果傳空陣列則任何狀態發生變化都不會觸發,此時useEffect的作用就類似class組件中的componentDidMount,所以發送請求通常也會在此執行,
清理副作用
在上面的操作中都不用清理的副作用,然而,有些副作用是需要去清理的,不清理會造成例外甚至記憶體泄漏,比如開啟定時器,如果不清理,則會多次開啟,從上面可以看到useEffect的第一個引數是一個回呼函式,可以在回呼函式中再回傳一個函式,該函式可以在狀態更新后第一個回呼函式執行之前呼叫,具體實作:
useEffect(() => {
// 設定副作用
return () => {
// 清理副作用
}
});
3、useContext:跨組件共享資料
React.createContext();創建一個TestContext物件
TestContext.Provider包裹子組件
資料放在<TestContext.Provider value=https://www.cnblogs.com/hymenhan/archive/2021/04/28/{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=https://www.cnblogs.com/hymenhan/archive/2021/04/28/{value}>
);
}
const Child1 = () => {
const value = useContext(TestContext);
return (
{(() => 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

至此資料實作共享了,但是可以看到在TestContext中的共享資料只要發生變化,子組件都會重新渲染,Child2并沒有系結資料,不希望他做無意義的渲染,可以使用React.memo解決,實作:
const Child2 = React.memo(() => {
return (
<div>
{(() => console.log('Child2-render'))()}
<h3>Child2</h3>
</div>
);
});

4、useCallback:性能優化
語法:
// useCallback(回呼函式,[依賴值])
const handleClick = useCallback(()=> {
// 做一些事
}, [value]);
useCallback回傳的是一個 memoized(快取)函式,在依賴不變的情況下,多次定義的時候,回傳的值是相同的,他的實作原理是當使用一組引數初次呼叫函式時,會快取引數和計算結果,當再次使用相同的引數呼叫該函式時,會直接回傳相應的快取結果,
優化性能例子:
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回傳的是一個memoized回呼函式,僅在其中系結的一個依賴項變化后才更改可防止不必要的渲染,在跨組件共享資料中舉例的事件是在父組件中點擊觸發,而現在是使用狀態提升,在父組件中傳遞方法供子組件呼叫,每次render時函式也會變化,導致子組件重新渲染,上面例子useCallback將函式進行包裹,依賴值未發生變化時會回傳快取的函式,配合React.memo即可優化無意義的渲染,
5、useMemo:性能優化
語法:
// useMemo(回呼函式,[依賴值])
useMemo(() => {
// 做一些事情
},[value]);
先看一個例子:
import React, { useState } from 'react'
const Test = ()=> {
const [value, setValue] = useState(0);
const [count, setCount] = useState(1);
const getDoubleCount = () => {
console.log('getDoubleCount進行計算了');
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發生變化它也重新進行了計算渲染,現在只需要將getDoubleCount使用useMemo進行包裹,如下:
import React, { useState, useMemo } from 'react'
const Test = ()=> {
const [value, setValue] = useState(0);
const [count, setCount] = useState(1);
const getDoubleCount = useMemo(() => {
console.log('getDoubleCount進行計算了');
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

現在getDoubleCount只有依賴的count發生變化時才會重新計算渲染,
useMemo和useCallback的共同點:
- 接收的引數都是一樣的,第一個是回呼函式,第二個是依賴的資料
- 它們都是當依賴的資料發生變化時才會重新計算結果,起到了快取作用
useMemo和useCallback的區別:
- useMemo計算結果是return回來的值,通常用于快取計算結果的值
- useCallback計算結果是一個函式,通常用于快取函式
6、useRef
用法:例如要實作點擊button按鈕使input輸入框獲得焦點:
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()也能實作同樣的效果,那為什么要設計一個新的hook?難道只是會了加上use,統一hook規范?
事實上,它們確實不一樣,
官網的說明如下:
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就像一個儲物箱,你可以隨意存放任何東西,再次渲染時它會去儲物箱找,createRef每次渲染都會回傳一個新的參考,而useRef每次都會回傳相同的參考,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/281173.html
標籤:其他
上一篇:HTML小技巧之——巧用<a>標簽錨鏈接(#錨點鏈接 #頁面特定位置 #錨點定位 #DOM定位 #hash路由中使用錨鏈接)
下一篇:探秘神奇的運動路徑影片 Motion Path