Redux 是一个流行的 JavaScript 状态管理库,它为应用程序提供了可预测的状态容器。无论您是新手还是经验丰富的开发者,都可以通过 Redux 来更好地管理应用程序的状态。
在本文中,我们将深入探讨 Redux 的核心概念、工作原理以及最佳实践,帮助您更好地理解和运用 Redux。
1. 为什么选择 Redux
- -React管理不断变化的state是非常困难的,需要通过constructor去定义state,要通过this.setState去 修改这个state。
- React是在视图层帮助我们解决了DOM的渲染过程,但是State依然是留给我们自己来管理。之前学 习过的组件传值props、context等等,
- 当没有使用redux的时候我们实现兄弟组件传值是比较麻烦的,代码很复杂冗余。
2. Redux 核心概念
- -Actions Actions 是描述发生了什么的纯对象。它们是应用发送数据到 store 的唯一方式。一般通过 type 字段来指明 action 的类型。
- Reducers
Reducers 指定了应用状态的变化如何响应 actions 并且发送到 store。它是一个纯函数,接收先前的状态和一个 action,并返回新的状态。 - Store
Store 是应用状态的单一来源。它负责:
保存应用的状态
允许通过 getState() 方法获取状态
允许通过 dispatch(action) 方法更新状态
允许通过 subscribe(listener) 方法注册监听器 - 单一数据源(Single Source of Truth)
整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。 - 不可变性(State is Read-Only)
State 是只读的,任何修改必须通过纯函数来进行。这意味着在修改 state 时需要返回新的 state 对象,而不是直接修改原始的 state。 - 单向数据流
整个应用中的 state 遵循严格的单向数据流。任何的数据流回馈都必须经过 action -> dispatcher -> store -> view 这样的流程。
这些核心概念构成了 Redux 库的基础,为开发者提供了一种清晰的、一致的模式来处理应用的状态。通过深入理解这些概念,开发者可以更好地利用 Redux 来构建复杂的前端应用程序。
3. 数据流向
-
触发 Action
- 当应用程序中的某个部分需要发起一些操作时(比如用户交互或者异步请求完成),会触发一个 action。这个 action是一个包含描述要执行操作的纯对象,通常具有一个 type 属性来指明操作类型,并可能携带其他相关数据。
-
Dispatch Action
- 通过使用 Redux store 中提供的 dispatch(action) 方法,将 action 分发到 store 中。
-
Reducer 处理 Action
- Store 中已经注册的 reducers 会根据接收到的 action 来处理状态的变化。根据 action 的类型和携带的数据,相应的
reducer 将计算出新的状态并返回。
- Store 中已经注册的 reducers 会根据接收到的 action 来处理状态的变化。根据 action 的类型和携带的数据,相应的
-
Store 更新
- 当 reducer 返回新的状态后,Redux 的 store 会被更新为最新的状态。
-
订阅状态更新
- 在 React 应用中,通常会使用 react-redux 提供的 useSelector 钩子或者 connect 方法来订阅
store 中 state 的更新。
- 在 React 应用中,通常会使用 react-redux 提供的 useSelector 钩子或者 connect 方法来订阅
-
组件重新渲染
- 由于状态的更新,与 store 中相关联的组件(通过 useSelector 或 connect)会感知到状态的变化,然后进行重新渲染。
代码:
下载依赖
yarn add react-redux redux
在store.js创建仓库
import { legacy_createStore as createStore } from 'redux'
const initialState = {} // 初识状态
// reducers
const reducer = (state = initialState, action) => { // reducer可以接受state,但是绝对不能改变state}
// 创建仓库
export default createStore(reducer)
在index项目入口文件引入store注入到根组件中
......
// 引入redux
import store from './redux/store'
import { Provider } from 'react-redux'
......
root.render(
<Provider store={store}>
<App />
</Provider>
);
);
编写action
Action 创建函数 就是生成 action 的方法。“action” 和 “action 创建函数” 这两个概念很容易混在一起,
使用时最好注意区分。
// store/index.js
const CHANGE_MSG = 'CHANGE_MSG'
export const a_changeMsg = payload => {
return {
type: CHANGE_MSG,
payload
}
}
编写reducer
// store/index.js
// reducer可以接受state,但是绝对不能改变state
const CHANGE_MSG = 'CHANGE_MSG'
const initialState = {
msg: '你好,李焕英'
}
......
const reducer = (state = initialState, action) => {
switch(action.type) {
case CHANGE_MSG:
return { ...state, msg: action.payload }
default:
return state
}
}
组件使用useSelector和useDispatch来操作redux
// Redux.jsx
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { a_changeMsg } from './store'
export default function Redux() {
const msg = useSelector(state => state.msg) // 使用hooks中的useSelector获取redux仓库中的state
const dispatch = useDispatch() // 使用hooks中的useDispatch调用redux=>action来修改数据
return (
<>
<h1>{ msg }</h1>
<button onClick={ () => dispatch(a_changeMsg('你好,贾玲她妈')) }>修改</button>
</>
)
}
4. 异步操作处理
- 如何处理异步操作,使用 thunk 或者 saga 等中间件进行异步 action 的处理。
- 如果redux中的状态是通过异步请求接口进行更新的
// store/index.js
import { legacy_createStore as createStore } from 'redux'
......
export const a_asyncChangeMsg = payload => {
setTimeout(() => { // 模拟异步请求接口
return {
type: CHANGE_MSG,
payload
}
}, 0)
}
......
export default createStore(reducer)
// Redux.jsx
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { a_asyncChangeMsg } from './store'
export default function Redux() {
const msg = useSelector(state => state.msg) // 使用hooks中的useSelector获取redux仓库中的state
const dispatch = useDispatch() // 使用hooks中的useDispatch调用redux=>action来修改数据
return (
<>
<h1>{ msg }</h1>
<button onClick={ () => dispatch(a_asyncChangeMsg('你好,贾玲她妈')) }>修改</button>
</>
)
}
- 在redux的工作流中,reducer是纯函数,内部不能进行异步操作。那么我们只能将异步的操作放入在
actionCreator中进行,但是如果直接这样写会报错哦 - 因为actions必须为普通对象,但实际上却是一个函数,你可能需要在创建store的时候添加一个中间件,比如redux-thunk
- actions必须为普通对象,但实际上却是一个函数,你可能需要在创建store的时候添 加一个中间件,比如redux-thunk
5. 与 React 结合使用
- 如何在 React 应用中使用 Redux,并介绍 react-redux 这个库的使用方法。
- 将redux代码改造一下
- 安装redux-thunk
- `yarn add redux-thunk`
- 修改store.js,引入中间件
import { legacy_createStore as createStore, applyMiddleware } from 'redux'
import { thunk } from 'redux-thunk' // 处理redux异步任务的中间件
......
// applyMiddleware 应用中间件,对store中的dispatch方法进行增强
export default createStore(reducer, applyMiddleware(thunk))
- 修改actions,返回一个函数
export const a_asyncChangeMsg = payload => {
return (dispatch, getState) => {
setTimeout(() => {
dispatch(a_changeMsg('你好,贾玲她妈'))
}, 1000)
}
}
- 测试异步代码
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { a_asyncChangeMsg } from './store'
export default function Redux() {
const msg = useSelector(state => state.msg) // 使用hooks中的useSelector获取redux仓库中的state
const dispatch = useDispatch() // 使用hooks中的useDispatch调用redux=>action来
修改数据
return (
<>
<h1>{ msg }</h1>
<button onClick={ () => dispatch(a_asyncChangeMsg()) }>修改</button>
</>
)
}
6. 常见问题
-
过度使用 Redux
有时候开发者会倾向于将所有的状态都存储在 Redux 中,这可能导致过度复杂化应用。在设计时应该权衡哪些状态适合放在 Redux 中,而哪些状态可以由组件本地管理。 -
过多的 Boilerplate 代码
-
传统的 Redux 存在大量的样板代码,如定义 action 类型、编写 reducer等。虽然可以通过使用辅助工具来减少这些重复性代码,但需要谨慎使用,并评估是否真正需要 Redux 来管理状态。
-
性能问题
当应用状态变得非常庞大时,Redux 可能造成性能瓶颈。合理地使用 selectors、避免不必要的重新渲染和状态更新、以及对状态树进行规范化都是解决性能问题的方法。 -
异步操作处理
虽然 Redux 自身只处理同步操作,但很多应用需要异步操作(如网络请求)。处理异步操作时,需要使用中间件或异步 action 创建函数。
结语
- 总结 Redux 的重要概念,并鼓励读者进一步深入学习。
在本篇博客中,我们将深入 Redux 的方方面面,旨在帮助读者全面理解 Redux 并能够在实际项目中应用它。通过本文,您将获得对 Redux 的深刻理解,并能够更加自信地使用它来管理应用程序的状态。