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

父組件修改count時,App依賴的count發(fā)生改變,所以,父組件App會重新渲染,
但是,此時子組件也重新渲染了,子組件并沒有依賴count,而且子組件此時是用memo包裹的,memo包裹的組件只有當其props發(fā)生改變時,才會重新渲染。
當子組件修改msg時,App組件也重新渲染了。
這是因為使用了useSelector,useSelector 監(jiān)聽的是 整個state數(shù)據,如果state里有任何數(shù)據發(fā)生變化,當前組件就會重新渲染。
而正常情況下,應該是:
- 只有 msg 改變時,子組件才會渲染;
- 只有count改變時,父組件才會渲染。
useSelector的第二個參數(shù):用來比較來決定組件是否需要渲染,當state發(fā)生改變時,useSelector 將本次映射的值與上次映射的值有沒有發(fā)生改變,有改變則重新渲染,沒有改變則不變。
// state發(fā)生變化時,父組件的useSelector判斷本次映射的count與上次映射的count是否一致,不一致則重新渲染,否則不重新渲染
const { count } = useSelector((state) => {
return {
count: state.counter.count
}
}, shallowEqual)
// state發(fā)生變化時,子組件的useSelector判斷本次映射的msg與上次映射的msg是否一致,不一致則重新渲染,否則不重新渲染
const { msg } = useSelector((state) => {
return {
msg: state.counter.msg
}
}, shallowEqual)
Reselect
Reselect 是一個專為 Redux 設計的記憶化選擇器庫,用于優(yōu)化從 Redux Store 中提取數(shù)據的性能。
它的核心思想是:通過緩存(memoization)避免重復計算,確保只有在相關狀態(tài)變化時,才重新執(zhí)行選擇器邏輯。
(1) 什么是記憶化(Memoization)?
- 定義:記憶化是一種優(yōu)化技術,用于緩存函數(shù)的輸入和輸出。當函數(shù)以相同的輸入再次調用時,直接返回緩存的結果,而不是重新計算。
- 目的:在 Redux 中,當狀態(tài)頻繁變化但某些派生數(shù)據未變化時,避免重復計算這些派生數(shù)據,從而提升性能。
(2) Reselect 的作用
場景:
- 當你的選擇器邏輯復雜(如:組合`個狀態(tài)片段、進行計算)時,Reselect 會自動緩存結果。
優(yōu)勢:
- 減少不必要的計算。
- 確保組件只在相關狀態(tài)變化時重新渲染。
- 保持選擇器邏輯的純凈(無副作用)。
Reselect 的核心 API:createSelector
用法:
import { createSelector } from 'reselect'
`輸入選擇器:從狀態(tài)中提取原始數(shù)據`
const selectUser = (state) => state.user 【state.user 是 Redux Store 中的一個 slice】
const selectPosts = (state) => state.posts 【state.posts 是 Redux Store 中的一個 slice】
`Reselect 的 createSelector 會按順序將 輸入選擇器的返回值,作為參數(shù)傳遞給轉換函數(shù)。
因此,轉換函數(shù)中的
user 對應 selectUser的返回值(即state.user)
posts 對應 selectPosts的返回值(即state.posts)`
const selectUserData = createSelector(
[selectUser, selectPosts], `【輸入選擇器數(shù)組】`
(user, posts) => ({ `【轉換函數(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的緩存機制- 緩存觸發(fā)條件:只有當
selectUser或selectPosts的返回值變化時,轉換函數(shù)才會重新執(zhí)行。
執(zhí)行流程:
首次調用:
- 執(zhí)行所有輸入選擇器(
selectUser和selectPosts)。 - 將結果傳遞給轉換函數(shù),計算并緩存最終值。
后續(xù)調用:
- 再次執(zhí)行輸入選擇器。
- 如果所有輸入選擇器的結果與上一次相同(嚴格相等
===),直接返回緩存值。 - 否則,重新執(zhí)行轉換函數(shù)并更新緩存。
結合shallowEqual
- 如果選擇器返回對象,可以結合
shallowEqual進一步優(yōu)化:
const { username, postCount } = useSelector(
selectUserData,
shallowEqual
)
高級用法:組合選擇器
多層選擇器
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) 輸入選擇器的順序
輸入選擇器的順序會影響緩存鍵的計算。例如:
const selectorA = createSelector( [selectX, selectY], (x, y) => x + y ); const selectorB = createSelector( [selectY, selectX], (y, x) => y + x );
selectorA和selectorB的緩存鍵不同(順序不同),即使邏輯相同也會重新計算。
(2) 緩存的生命周期
- 全局緩存:Reselect 的緩存是全局的,與組件實例無關。
- 狀態(tài)變化時:只要輸入選擇器的結果變化,緩存就會失效。
(3) 深層嵌套狀態(tài)的處理
- 如果狀態(tài)是深層嵌套的對象,輸入選擇器需要提取具體的字段:
const selectDeepData = createSelector( [(state) => state.deep.nested.data], (data) => data.value );
總結:
Reselect 的本質:通過記憶化選擇器,避免重復計算,優(yōu)化性能。
適用場景:當選擇器邏輯復雜、依賴多個狀態(tài)片段或需要組合數(shù)據時。
最佳實踐:
- 優(yōu)先使用
createSelector創(chuàng)建選擇器。 - 保持輸入選擇器的簡單性,將復雜邏輯放在轉換函數(shù)中。
- 結合
useSelector和shallowEqual進一步優(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>
)
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
在React中實現(xiàn)代碼熱重載(HMR)的操作步驟
在現(xiàn)代 React 開發(fā)中,代碼熱重載(HMR)是一項極大提升開發(fā)效率的技術,它允許在不刷新整個頁面的情況下,實時更新模塊代碼,從而保留應用狀態(tài),避免因頁面刷新導致的狀態(tài)丟失,本文將深入探討如何在 React 項目中實現(xiàn) HMR,包括其原理、配置步驟以及最佳實踐2025-05-05
深入理解React Native原生模塊與JS模塊通信的幾種方式
本篇文章主要介紹了深入理解React Native原生模塊與JS模塊通信的幾種方式,具有一定的參考價值,有興趣的可以了解一下2017-07-07

