redux中的hooks使用详解

 更新时间:2025年07月29日 08:57:26   作者:改了一个昵称  
useSelector从Redux提取状态,配合shallowEqual和memo优化渲染性能;Reselect通过缓存减少重复计算,适用于复杂选择器逻辑,提升效率

useSelector

基础使用

  • 功能:useSelector 是 react-redux 提供的 Hook,用于从 Redux Store 中选择(提取)状态数据
  • 参数一:接收一个 选择器函数,该函数接收完整的 Redux Store 状态(state)作为参数,返回需要的数据
  • 参数二: 可传入shallowEqual,比较来决定组件是否重新渲染
  • 返回值:返回 选择器函数的结果(这里是 { count: … } 对象)
import { useSelector } from 'react-redux'
import { memo } from 'react'

const App = memo(() => {
	` 使用 useSelector 将 Redux Store 中的数据映射到组件内 `
	` state 参数:代表整个 Redux Store 的根状态(root state) `
	const { count } = useSelector((state) => {
		return {
			`假设你的 Redux Store 中有一个名为 counter 的 slice(分片),该 slice 的状态中包含 count 字段`
			count: state.counter.count
		}
	})

	return (
		<div>
			<h2>count: {count}</h2>
		</div>
	)
})

这里memo的作用:

  • 用于对组件进行浅层 props 比较,避免不必要的重新渲染。

适用场景:

  • 如果 App 组件的父组件频繁渲染,但 App 的 props 未变化,memo 可以优化性能。

memo 与 useSelector 的关系:

  • 即使 memo 包裹了组件,useSelector 仍会在 Redux Store 的 count 变化时触发组件更新(因为内部状态变化)。

第二个参数 shallowEqual

  • 功能:shallowEqual 是 react-redux 提供的浅比较函数,用于,比较两次选择器返回的结果,是否 “浅层相等”
  • 适用场景:当你的选择器返回一个对象或数组时,shallowEqual 会逐个比较对象的每个属性(或数组的每个元素)是否严格相等(===)。
  • 深层嵌套无效:shallowEqual 仅比较 第一层属性。如果对象有深层嵌套(如 { data: { count: 1 } }),深层变化不会被检测到。

父组件修改count时,App依赖的count发生改变,所以,父组件App会重新渲染,

但是,此时子组件也重新渲染了,子组件并没有依赖count,而且子组件此时是用memo包裹的,memo包裹的组件只有当其props发生改变时,才会重新渲染。

当子组件修改msg时,App组件也重新渲染了。

这是因为使用了useSelector,useSelector 监听的是 整个state数据,如果state里有任何数据发生变化,当前组件就会重新渲染。

而正常情况下,应该是:

  • 只有 msg 改变时,子组件才会渲染;
  • 只有count改变时,父组件才会渲染。

useSelector的第二个参数:用来比较来决定组件是否需要渲染,当state发生改变时,useSelector 将本次映射的值与上次映射的值有没有发生改变,有改变则重新渲染,没有改变则不变。

// state发生变化时,父组件的useSelector判断本次映射的count与上次映射的count是否一致,不一致则重新渲染,否则不重新渲染
const { count } = useSelector((state) => {
	return {
		count: state.counter.count
	}
}, shallowEqual)

// state发生变化时,子组件的useSelector判断本次映射的msg与上次映射的msg是否一致,不一致则重新渲染,否则不重新渲染
const { msg } = useSelector((state) => {
	return {
		msg: state.counter.msg
	}
}, shallowEqual)

Reselect

Reselect 是一个专为 Redux 设计的记忆化选择器库,用于优化从 Redux Store 中提取数据的性能。

它的核心思想是:通过缓存(memoization)避免重复计算,确保只有在相关状态变化时,才重新执行选择器逻辑。

(1) 什么是记忆化(Memoization)?

  • 定义:记忆化是一种优化技术,用于缓存函数的输入和输出。当函数以相同的输入再次调用时,直接返回缓存的结果,而不是重新计算。
  • 目的:在 Redux 中,当状态频繁变化但某些派生数据未变化时,避免重复计算这些派生数据,从而提升性能。

(2) Reselect 的作用

场景

  • 当你的选择器逻辑复杂(如:组合`个状态片段、进行计算)时,Reselect 会自动缓存结果。

优势

  • 减少不必要的计算。
  • 确保组件只在相关状态变化时重新渲染。
  • 保持选择器逻辑的纯净(无副作用)。

Reselect 的核心 API:createSelector

用法:

import { createSelector } from 'reselect'

`输入选择器:从状态中提取原始数据`
const selectUser = (state) => state.user       【state.user 是 Redux Store 中的一个 slice】
const selectPosts = (state) => state.posts     【state.posts 是 Redux Store 中的一个 slice】

`Reselect 的 createSelector 会按顺序将 输入选择器的返回值,作为参数传递给转换函数。
 因此,转换函数中的
   user  对应 selectUser的返回值(即state.user)
   posts 对应 selectPosts的返回值(即state.posts)`
const selectUserData = createSelector(
	[selectUser, selectPosts],    `【输入选择器数组】`
	(user, posts) => ({           `【转换函数】`
		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 的缓存机制
  • 缓存触发条件:只有当 selectUserselectPosts 的返回值变化时,转换函数才会重新执行。

执行流程:

首次调用

  • 执行所有输入选择器(selectUserselectPosts)。
  • 将结果传递给转换函数,计算并缓存最终值。

后续调用

  • 再次执行输入选择器。
  • 如果所有输入选择器的结果与上一次相同(严格相等 ===),直接返回缓存值。
  • 否则,重新执行转换函数并更新缓存。

结合shallowEqual

  • 如果选择器返回对象,可以结合 shallowEqual 进一步优化:
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,
	})
)

带参数的选择器

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
);
  • selectorAselectorB 的缓存键不同(顺序不同),即使逻辑相同也会重新计算。

(2) 缓存的生命周期

  • 全局缓存:Reselect 的缓存是全局的,与组件实例无关。
  • 状态变化时:只要输入选择器的结果变化,缓存就会失效。

(3) 深层嵌套状态的处理

  • 如果状态是深层嵌套的对象,输入选择器需要提取具体的字段:
const selectDeepData = createSelector(
  [(state) => state.deep.nested.data],
  (data) => data.value
);

总结:

Reselect 的本质:通过记忆化选择器,避免重复计算,优化性能。

适用场景:当选择器逻辑复杂、依赖多个状态片段或需要组合数据时。

最佳实践

  • 优先使用 createSelector 创建选择器。
  • 保持输入选择器的简单性,将复杂逻辑放在转换函数中。
  • 结合 useSelectorshallowEqual 进一步优化渲染性能。

useDispatch获取dispatch函数

  • 直接用 useDispatch 获取 dispatch ,派发action 即可
` 派发操作 `
const dispatch = useDispatch()

function calNumber (num) {
	dispatch(addNumberAction(num))
}

return (
	<div>
		<h2>count:{count}</h2>
		<button onClick={e => calNumber(1)}> +1 </button>
	</div>
)

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:

相关文章

  • React+Antd 实现可增删改表格的示例

    React+Antd 实现可增删改表格的示例

    这篇文章主要介绍了React+Antd实现可增删改表格的示例,帮助大家更好的理解和学习使用React,感兴趣的朋友可以了解下
    2021-04-04
  • React 中hooks之 React.memo 和 useMemo用法示例总结

    React 中hooks之 React.memo 和 useMemo用法示例总结

    React.memo是一个高阶组件,用于优化函数组件的性能,通过记忆组件渲染结果来避免不必要的重新渲染,合理使用React.memo和useMemo可以显著提升React应用的性能,本文介绍React 中hooks之 React.memo 和 useMemo用法总结,感兴趣的朋友一起看看吧
    2025-01-01
  • 基于React实现虚拟滚动的方案详解

    基于React实现虚拟滚动的方案详解

    这篇文章将以固定高度和非固定高度两种场景展开React中虚拟滚动的实现,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-03-03
  • 解决React hook 'useState' cannot be called in a class component报错

    解决React hook 'useState' cannot be called in 

    这篇文章主要为大家介绍了React hook 'useState' cannot be called in a class component报错解决方法,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 基于React封装一个验证码输入控件

    基于React封装一个验证码输入控件

    邮箱、手机验证码输入是许多在线服务和网站常见的安全验证方式之一,本文主要来和大家讨论一下如何使用React封装一个验证码输入控件,感兴趣的可以了解下
    2024-03-03
  • react纯函数组件setState更新页面不刷新的解决

    react纯函数组件setState更新页面不刷新的解决

    在开发过程中,经常遇到组件数据无法更新,本文主要介绍了react纯函数组件setState更新页面不刷新的解决,感兴趣的可以了解一下
    2021-06-06
  • React前端框架实现原理的理解

    React前端框架实现原理的理解

    React是前端开发每天都用的前端框架,自然要深入掌握它的原理。我用 React 也挺久了,这篇文章就来总结一下我对 react 原理的理解,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2022-07-07
  • React路由组件三种传参方式分析讲解

    React路由组件三种传参方式分析讲解

    本文主要介绍了React组件通信之路由传参(react-router-dom),文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • react 实现页面代码分割、按需加载的方法

    react 实现页面代码分割、按需加载的方法

    本篇文章主要介绍了react 实现页面代码分割、按需加载的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • react以create-react-app为基础创建项目

    react以create-react-app为基础创建项目

    这篇文章主要介绍了react以create-react-app为基础创建项目,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03

最新评论