redux中的hooks使用詳解
useSelector
基礎(chǔ)使用
- 功能:
useSelector是 react-redux 提供的 Hook,用于從 Redux Store 中選擇(提?。顟B(tài)數(shù)據(jù) - 參數(shù)一:接收一個(gè) 選擇器函數(shù),該函數(shù)接收完整的
Redux Store 狀態(tài)(state)作為參數(shù),返回需要的數(shù)據(jù) - 參數(shù)二: 可傳入
shallowEqual,比較來(lái)決定組件是否重新渲染 - 返回值:返回 選擇器函數(shù)的結(jié)果(這里是 { count: … } 對(duì)象)
import { useSelector } from 'react-redux'
import { memo } from 'react'
const App = memo(() => {
` 使用 useSelector 將 Redux Store 中的數(shù)據(jù)映射到組件內(nèi) `
` state 參數(shù):代表整個(gè) Redux Store 的根狀態(tài)(root state) `
const { count } = useSelector((state) => {
return {
`假設(shè)你的 Redux Store 中有一個(gè)名為 counter 的 slice(分片),該 slice 的狀態(tài)中包含 count 字段`
count: state.counter.count
}
})
return (
<div>
<h2>count: {count}</h2>
</div>
)
})
這里memo的作用:
- 用于對(duì)組件進(jìn)行淺層 props 比較,避免不必要的重新渲染。
適用場(chǎng)景:
- 如果 App 組件的父組件頻繁渲染,但 App 的 props 未變化,memo 可以?xún)?yōu)化性能。
memo 與 useSelector 的關(guān)系:
- 即使 memo 包裹了組件,useSelector 仍會(huì)在 Redux Store 的 count 變化時(shí)觸發(fā)組件更新(因?yàn)閮?nèi)部狀態(tài)變化)。
第二個(gè)參數(shù) shallowEqual
- 功能:
shallowEqual是 react-redux 提供的淺比較函數(shù),用于,比較兩次選擇器返回的結(jié)果,是否 “淺層相等”。 - 適用場(chǎng)景:當(dāng)你的選擇器返回一個(gè)對(duì)象或數(shù)組時(shí),shallowEqual 會(huì)逐個(gè)比較對(duì)象的每個(gè)屬性(或數(shù)組的每個(gè)元素)是否嚴(yán)格相等(===)。
- 深層嵌套無(wú)效:
shallowEqual 僅比較 第一層屬性。如果對(duì)象有深層嵌套(如 { data: { count: 1 } }),深層變化不會(huì)被檢測(cè)到。

父組件修改count時(shí),App依賴(lài)的count發(fā)生改變,所以,父組件App會(huì)重新渲染,
但是,此時(shí)子組件也重新渲染了,子組件并沒(méi)有依賴(lài)count,而且子組件此時(shí)是用memo包裹的,memo包裹的組件只有當(dāng)其props發(fā)生改變時(shí),才會(huì)重新渲染。
當(dāng)子組件修改msg時(shí),App組件也重新渲染了。
這是因?yàn)槭褂昧藆seSelector,useSelector 監(jiān)聽(tīng)的是 整個(gè)state數(shù)據(jù),如果state里有任何數(shù)據(jù)發(fā)生變化,當(dāng)前組件就會(huì)重新渲染。
而正常情況下,應(yīng)該是:
- 只有 msg 改變時(shí),子組件才會(huì)渲染;
- 只有count改變時(shí),父組件才會(huì)渲染。
useSelector的第二個(gè)參數(shù):用來(lái)比較來(lái)決定組件是否需要渲染,當(dāng)state發(fā)生改變時(shí),useSelector 將本次映射的值與上次映射的值有沒(méi)有發(fā)生改變,有改變則重新渲染,沒(méi)有改變則不變。
// state發(fā)生變化時(shí),父組件的useSelector判斷本次映射的count與上次映射的count是否一致,不一致則重新渲染,否則不重新渲染
const { count } = useSelector((state) => {
return {
count: state.counter.count
}
}, shallowEqual)
// state發(fā)生變化時(shí),子組件的useSelector判斷本次映射的msg與上次映射的msg是否一致,不一致則重新渲染,否則不重新渲染
const { msg } = useSelector((state) => {
return {
msg: state.counter.msg
}
}, shallowEqual)
Reselect
Reselect 是一個(gè)專(zhuān)為 Redux 設(shè)計(jì)的記憶化選擇器庫(kù),用于優(yōu)化從 Redux Store 中提取數(shù)據(jù)的性能。
它的核心思想是:通過(guò)緩存(memoization)避免重復(fù)計(jì)算,確保只有在相關(guān)狀態(tài)變化時(shí),才重新執(zhí)行選擇器邏輯。
(1) 什么是記憶化(Memoization)?
- 定義:記憶化是一種優(yōu)化技術(shù),用于緩存函數(shù)的輸入和輸出。當(dāng)函數(shù)以相同的輸入再次調(diào)用時(shí),直接返回緩存的結(jié)果,而不是重新計(jì)算。
- 目的:在 Redux 中,當(dāng)狀態(tài)頻繁變化但某些派生數(shù)據(jù)未變化時(shí),避免重復(fù)計(jì)算這些派生數(shù)據(jù),從而提升性能。
(2) Reselect 的作用
場(chǎng)景:
- 當(dāng)你的選擇器邏輯復(fù)雜(如:組合`個(gè)狀態(tài)片段、進(jìn)行計(jì)算)時(shí),Reselect 會(huì)自動(dòng)緩存結(jié)果。
優(yōu)勢(shì):
- 減少不必要的計(jì)算。
- 確保組件只在相關(guān)狀態(tài)變化時(shí)重新渲染。
- 保持選擇器邏輯的純凈(無(wú)副作用)。
Reselect 的核心 API:createSelector
用法:
import { createSelector } from 'reselect'
`輸入選擇器:從狀態(tài)中提取原始數(shù)據(jù)`
const selectUser = (state) => state.user 【state.user 是 Redux Store 中的一個(gè) slice】
const selectPosts = (state) => state.posts 【state.posts 是 Redux Store 中的一個(gè) slice】
`Reselect 的 createSelector 會(huì)按順序?qū)?輸入選擇器的返回值,作為參數(shù)傳遞給轉(zhuǎn)換函數(shù)。
因此,轉(zhuǎn)換函數(shù)中的
user 對(duì)應(yīng) selectUser的返回值(即state.user)
posts 對(duì)應(yīng) selectPosts的返回值(即state.posts)`
const selectUserData = createSelector(
[selectUser, selectPosts], `【輸入選擇器數(shù)組】`
(user, posts) => ({ `【轉(zhuǎn)換函數(shù)】`
username: user.name,
postCount: posts.length,
})
)
某組件如下:
import { useSelector } from 'react-redux'
import { selectUserData } from './selectors'
const Component = () => {
const { username, postCount } = useSelector(selectUserData)
return (
<div>
<h3>{username}</h3>
<p>Posts: {postCount}</p>
</div>
)
}
Reselect的緩存機(jī)制- 緩存觸發(fā)條件:只有當(dāng)
selectUser或selectPosts的返回值變化時(shí),轉(zhuǎn)換函數(shù)才會(huì)重新執(zhí)行。
執(zhí)行流程:
首次調(diào)用:
- 執(zhí)行所有輸入選擇器(
selectUser和selectPosts)。 - 將結(jié)果傳遞給轉(zhuǎn)換函數(shù),計(jì)算并緩存最終值。
后續(xù)調(diào)用:
- 再次執(zhí)行輸入選擇器。
- 如果所有輸入選擇器的結(jié)果與上一次相同(嚴(yán)格相等
===),直接返回緩存值。 - 否則,重新執(zhí)行轉(zhuǎn)換函數(shù)并更新緩存。
結(jié)合shallowEqual
- 如果選擇器返回對(duì)象,可以結(jié)合
shallowEqual進(jìn)一步優(yōu)化:
const { username, postCount } = useSelector(
selectUserData,
shallowEqual
)
高級(jí)用法:組合選擇器
多層選擇器
const selectUser = (state) => state.user
const selectPosts = (state) => state.posts
const selectUserData = createSelector(
[selectUser, selectPosts],
(user, posts) => ({
username: user.name,
postCount: posts.length,
})
)
const selectUserStats = createSelector(
[selectUserData],
(userData) => ({
avgPostsPerDay: userData.postCount / 30,
})
)
帶參數(shù)的選擇器
const selectPostById = (postId) => createSelector(
[(state) => state.posts],
(posts) => posts.find(post => post.id === postId)
)
// 組件中使用
const Component = ({ postId }) => {
const post = useSelector(() => selectPostById(postId))
return <div>{post.title}</div>
}
深入解析:Reselect 的工作原理
(1) 輸入選擇器的順序
輸入選擇器的順序會(huì)影響緩存鍵的計(jì)算。例如:
const selectorA = createSelector( [selectX, selectY], (x, y) => x + y ); const selectorB = createSelector( [selectY, selectX], (y, x) => y + x );
selectorA和selectorB的緩存鍵不同(順序不同),即使邏輯相同也會(huì)重新計(jì)算。
(2) 緩存的生命周期
- 全局緩存:Reselect 的緩存是全局的,與組件實(shí)例無(wú)關(guān)。
- 狀態(tài)變化時(shí):只要輸入選擇器的結(jié)果變化,緩存就會(huì)失效。
(3) 深層嵌套狀態(tài)的處理
- 如果狀態(tài)是深層嵌套的對(duì)象,輸入選擇器需要提取具體的字段:
const selectDeepData = createSelector( [(state) => state.deep.nested.data], (data) => data.value );
總結(jié):
Reselect 的本質(zhì):通過(guò)記憶化選擇器,避免重復(fù)計(jì)算,優(yōu)化性能。
適用場(chǎng)景:當(dāng)選擇器邏輯復(fù)雜、依賴(lài)多個(gè)狀態(tài)片段或需要組合數(shù)據(jù)時(shí)。
最佳實(shí)踐:
- 優(yōu)先使用
createSelector創(chuàng)建選擇器。 - 保持輸入選擇器的簡(jiǎn)單性,將復(fù)雜邏輯放在轉(zhuǎn)換函數(shù)中。
- 結(jié)合
useSelector和shallowEqual進(jìn)一步優(yōu)化渲染性能。
useDispatch獲取dispatch函數(shù)
- 直接用
useDispatch獲取 dispatch ,派發(fā)action 即可
` 派發(fā)操作 `
const dispatch = useDispatch()
function calNumber (num) {
dispatch(addNumberAction(num))
}
return (
<div>
<h2>count:{count}</h2>
<button onClick={e => calNumber(1)}> +1 </button>
</div>
)
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
React+Antd 實(shí)現(xiàn)可增刪改表格的示例
這篇文章主要介紹了React+Antd實(shí)現(xiàn)可增刪改表格的示例,幫助大家更好的理解和學(xué)習(xí)使用React,感興趣的朋友可以了解下2021-04-04
React?中hooks之?React.memo?和?useMemo用法示例總結(jié)
React.memo是一個(gè)高階組件,用于優(yōu)化函數(shù)組件的性能,通過(guò)記憶組件渲染結(jié)果來(lái)避免不必要的重新渲染,合理使用React.memo和useMemo可以顯著提升React應(yīng)用的性能,本文介紹React?中hooks之?React.memo?和?useMemo用法總結(jié),感興趣的朋友一起看看吧2025-01-01
基于React實(shí)現(xiàn)虛擬滾動(dòng)的方案詳解
這篇文章將以固定高度和非固定高度兩種場(chǎng)景展開(kāi)React中虛擬滾動(dòng)的實(shí)現(xiàn),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-03-03
解決React?hook?'useState'?cannot?be?called?in?
這篇文章主要為大家介紹了React?hook?'useState'?cannot?be?called?in?a?class?component報(bào)錯(cuò)解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
react純函數(shù)組件setState更新頁(yè)面不刷新的解決
在開(kāi)發(fā)過(guò)程中,經(jīng)常遇到組件數(shù)據(jù)無(wú)法更新,本文主要介紹了react純函數(shù)組件setState更新頁(yè)面不刷新的解決,感興趣的可以了解一下2021-06-06
react 實(shí)現(xiàn)頁(yè)面代碼分割、按需加載的方法
本篇文章主要介紹了react 實(shí)現(xiàn)頁(yè)面代碼分割、按需加載的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
react以create-react-app為基礎(chǔ)創(chuàng)建項(xiàng)目
這篇文章主要介紹了react以create-react-app為基礎(chǔ)創(chuàng)建項(xiàng)目,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03

