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+Ts实现二次封装组件

    React+Ts实现二次封装组件

    本文主要介绍了React+Ts实现二次封装组件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • React运行机制超详细讲解

    React运行机制超详细讲解

    React 作为当下最为流行的前端开发框架之一,使用它可以快速构建大型 Web 应用,加上其出色的性能表现,使得众多互联网公司对它格外地青睐,这篇文章主要介绍了React运行机制
    2022-11-11
  • react实现动态表单

    react实现动态表单

    这篇文章主要为大家详细介绍了react实现动态表单,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • 浅谈React Native Flexbox布局(小结)

    浅谈React Native Flexbox布局(小结)

    这篇文章主要介绍了浅谈React Native Flexbox布局(小结),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • react动态路由的实现示例

    react动态路由的实现示例

    React中动态路由通过ReactRouter库实现,根据应用状态或用户交互动态显示或隐藏组件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-11-11
  • React组件的数据挂载实现方式

    React组件的数据挂载实现方式

    React中state为组件内部可变数据,通过setState更新;props为外部传入的不可变参数,两者均可触发渲染,但state由组件维护,props由父组件控制,列表需用key优化性能,dangerouslySetInnerHTML用于内联HTML
    2025-07-07
  • 在React中实现代码热重载(HMR)的操作步骤

    在React中实现代码热重载(HMR)的操作步骤

    在现代 React 开发中,代码热重载(HMR)是一项极大提升开发效率的技术,它允许在不刷新整个页面的情况下,实时更新模块代码,从而保留应用状态,避免因页面刷新导致的状态丢失,本文将深入探讨如何在 React 项目中实现 HMR,包括其原理、配置步骤以及最佳实践
    2025-05-05
  • 深入理解React Native原生模块与JS模块通信的几种方式

    深入理解React Native原生模块与JS模块通信的几种方式

    本篇文章主要介绍了深入理解React Native原生模块与JS模块通信的几种方式,具有一定的参考价值,有兴趣的可以了解一下
    2017-07-07
  • React数据传递之组件内部通信的方法

    React数据传递之组件内部通信的方法

    这篇文章主要介绍了React数据传递之组件内部通信的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • React Hooks项目实战

    React Hooks项目实战

    React Hooks是React 16.8版本引入的新特性,它使得在函数组件中也能够使用状态(state)和其他React特性,本文就来详细介绍一下React Hooks项目实战,感兴趣的可以了解一下
    2023-11-11

最新评论