Redux
(全称为Redux
)是一个基于状态管理的JavaScript
库,它可以用来构建可重用的、可维护的代码。Redux
主要用于处理复杂的应用程序中的状态管理,它能够自动地处理应用程序中的更改,并在需要时更新视图。
Redux
使用一种被称为“状态树”的数据结构来管理应用程序的状态。状态树中的每个节点都代表应用程序中的一个状态,而每个状态都可以有自己的副本和父节点。当应用程序中的某个状态发生更改时,它会通过一个被称为“动作”的API
来更新状态树,并触发有关的副作用。这意味着,当一个部分的状态更改时,不会影响其他部分。
Redux
使用一个中央控制器来协调应用程序中的所有状态更改。这个控制器可以监听所有的动作,并根据需要更改状态。Redux
也提供了一些工具,如树形状态视图和状态转换器,来帮助开发人员处理复杂的状态管理。
总的来说,Redux
是一种非常强大和灵活的库,可以用来构建复杂的应用程序。它能够使开发人员专注于应用程序的逻辑,并帮助他们更轻松地管理应用程序中的状态。
具体详情请参考:Redux 中文官网
什么情况下使用Redux?
Redux
通常用于处理复杂的应用程序,特别是那些具有大量状态和复杂逻辑的应用程序。以下是一些 Redux
可能适合您的应用程序的示例:
- 您的应用程序具有复杂的路由和状态管理逻辑。
- 您需要处理大量的状态和更改,并且不希望在代码中硬编码它们。
- 您需要更好地组织和管理您的状态和副作用。
- 您需要一种方式来组合和重用不同的状态和动作。
当您考虑使用 Redux
时,需要确定您的应用程序是否适合使用 Redux
。如果您的应用程序符合上述条件,那么 Reducer
、Middleware
和 other
组件可以帮助您更轻松地管理应用程序的状态和逻辑。
Redux的工作原理
Redux
的工作原理是通过将应用程序的状态和动作拆分成独立的组件来实现的。在 Reducer
中,您可以使用一组函数来更改应用程序的状态。每个函数都接收一个 state
和一个 action
,并返回一个新的 state
。Redux
通过组合这些 state
来控制应用程序的状态。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HeKPRhDI-1684308971073)(D:\GeniusNotes\ReactNote\react_staging\img\redux原理图.png)]
在 Redux
中,还有一些中间件(称为 Connect
),它们用于协调 Redux
中的组件。Connect
可以帮助您将应用程序的不同部分连接在一起,并提供一些功能,如数据流和状态管理。 当 Redux
中的某个组件触发一个动作时,它会通过 Action
来通知 Redux
。这个 Action
可以被 Redux
中的其他组件所读取和处理。Redux
还提供了一些其他的工具和库,如 Connect
、Hooks
和 Reducer
,来帮助开发人员处理复杂的应用程序逻辑。
Redux
的三个核心概念
Action
:Action
是一种数据类型,它表示一个状态更改操作。Action
可以被Redux
中的所有组件读取和处理。Action
可以包含任何类型的数据,例如字符串、数字或映射。Reducer
:Reducer
是Redux
中用于修改state
的组件。Reducer
接收一个state
和一个action
,并返回一个new state
。Reducer
的作用是通过更改状态来控制Redux
中的应用程序逻辑。Reducer
通常由一组函数组成,这些函数接收一个state
和一个action
,并返回一个new state
。Store
:Store
是Redux
中用于存储和管理state
和动作的组件。Store
是一个全局的对象,它可以存储应用程序的所有state
和动作。Store
可以被Redux
中的其他组件所读取和处理。Store
还负责协调Redux
中的所有state
的更改。
通过求和案例循序渐进了解Redux
以下是案列的图示:
该组件(包含一个下拉框,四个功能按钮)功能很简单:
- 选择下拉框,选择要加减的具体数值
- 点击按钮
+
,将下拉框的数值进行相加 - 点击按钮
-
,将下拉框的数值进行相减 - 点击按钮
当前求和为奇数为
,当和是奇数的时候才将下拉框的值进行相加,否则不操作 - 点击按钮
异步加
,点击按钮之后,隔一段时间才将下拉框的值进行相加
1、使用普通的react程序实现该功能
文件:components/Count/index.jsx
import React, { Component } from 'react'
export default class Count extends Component {
state = { count: 0 }
increment = () => {
// 普通加
// 1、获取用户选择的值
const { value } = this.selectNumber
// 2、获取原来状态里面的值
const { count } = this.state
// 3、进行计算并更新状态值
this.setState({ count: count + value * 1 })
}
decrement = () => {
// 普通减
// 1、获取用户选择的值
const { value } = this.selectNumber
// 2、获取原来状态里面的值
const { count } = this.state
// 3、进行计算并更新状态值
this.setState({ count: count - value * 1 })
}
incrementIfOdd = () => {
// 当前求和为奇数为
// 1、获取用户选择的值
const { value } = this.selectNumber
// 2、获取原来状态里面的值
const { count } = this.state
// 3、进行计算并更新状态值
if (store.getState() % 2 !== 0) {
this.setState({ count: count + value * 1 })
}
}
incrementAsync = () => {
// 异步加
// 1、获取用户选择的值
const { value } = this.selectNumber
// 2、获取原来状态里面的值
const { count } = this.state
// 3、进行计算并更新状态值
setTimeout(() => {
this.setState({ count: count + value * 1 })
}, 500)
}
render() {
return (
<div>
<h1>当前求和为:{this.state.count}</h1>
<select ref={(c) => (this.selectNumber = c)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数为</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
)
}
}
文件:App.jsx
import React, { Component } from 'react'
import Count from './components/Count'
export default class App extends Component {
render() {
return (
<div>
<Count/>
</div>
)
}
}
以上代码就是通过普通的state
实现的案例功能。
2、Redux精简版改进求和案例
- 步骤1:安装依赖包
npm i redux
- 步骤2:创建文件:
redux/store.js
/** 该文件专门用于暴露store对象,整个应用只有一个store对象 */
// 引入createStore,专门用于创建redux中最为核心的store对象
import { createStore } from 'redux'
// 引入为Count组件服务的reducer
import countReducer from './count_reducer'
// 暴露store
export default createStore(countReducer)
- 步骤3:创建文件:
redux/count_reducer.js
/**
* 1、该文件是用于创建一个为count组件服务的reducer,reducer的本质就是一个函数
* 2、reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/
const initCount = 0 // 当reducer做初始化的时候,将初始值undefined改为0
export default function countReducer( preState = initCount, action) {
// 从action对象中获取type和data
const { type, data } = action;
// 根据type类型,如何加工数据
switch (type) {
case 'increment': // 如果是加,使用以下逻辑加工数据
return preState + data;
case 'decrement': // 如果是减,使用以下逻辑加工数据
return preState - data;
default:
return preState ;
}
}
- 步骤4:修改Count组件逻辑
import React, { Component } from 'react'
import store from '../../redux/store' // 引入store
export default class Count extends Component {
// state = { count: 0 } // 使用了redux的状态后,就不需要组件内部的state了
componentDidMount() {
// 监测redux中状态的变化,只要变化,就调用render
store.subscribe(()=>{
this.setState({}) // 当调用这个方法后,页面才会更新
})
}
increment = () => {
// 普通加
// 1、获取用户选择的值
const { value } = this.selectNumber
// 2、进行计算并更新状态值,通知redux执行相加逻辑
store.dispatch({type:'increment',data: value * 1})
}
decrement = () => {
// 普通减
// 1、获取用户选择的值
const { value } = this.selectNumber
// 2、进行计算并更新状态值,通知redux执行相减逻辑
store.dispatch({type:'decrement',data: value * 1})
}
incrementIfOdd = () => {
// 当前求和为奇数为
// 1、获取用户选择的值
const { value } = this.selectNumber
// 2、进行计算并更新状态值
if (store.getState() % 2 !== 0) {
// 通知redux执行相加逻辑
store.dispatch({type:'increment',data: value * 1})
}
}
incrementAsync = () => {
// 异步加
// 1、获取用户选择的值
const { value } = this.selectNumber
// 2、进行计算并更新状态值
setTimeout(() => {
// 通知redux执行相加逻辑
store.dispatch({type:'increment',data: value * 1})
}, 500)
}
render() {
// store.getState(),这个API用于获取redux的状态
return (
<div>
<h1>当前求和为:{store.getState()}</h1>
<select ref={(c) => (this.selectNumber = c)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数为</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
)
}
}
- 步骤5:优化redux的监听
为了让整个应用都监听redux
的变化,我们需要将订阅redux
变化的逻辑写到入口文件中
文件:index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './redux/store'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// 监听全局redux,一但发生改变就会更新页面
store.subscribe(()=> {
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
})
- 小节总结
(1). 去除Count
组件自身的状态
(2). src
下建立:
-src
|----redux
|----store.js
|----count_reducer.js
(3).store.js
:
1).引入redux
中的createStore
函数,创建一个store
2).createStore
调用时需要传入一个为其服务的reducer
3).记得暴露store
对象
(4).count_reducer.js
:
1). reducer
的本质是一个函数,接收:preState
,action
,返回加工后的状态
2). reducer
有两个作用:初始化状态,加工状态
3). reducer
被第一次调用时,是store
自动触发的,传递的preState
是undefined
,传递的action是:{type:'@@REDUX/INIT_a.1.2.3'}
(5).在index.js
中监测store
中状态的改变,一旦发生改变重新渲染<App/>
备注:redux
只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。
3、Redux完整版求和案例
完整版比精简版多了几个文件:constant.js
、count_action.js
- 定义常量文件:
constant.js
,便于管理
// 该模块是用于定义action对象的type类型的常量值
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
- 定义管理action对象的文件,层次分明
// 该文件专门为Count组件生成action对象
import {INCREMENT,DECREMENT} from './constant'
export const createIncrementAction = data => ({ type: INCREMENT,data})
export const createDecrementAction = data => ({ type: DECREMENT,data})
- 完善组件内容
import React, { Component } from 'react'
import store from '../../redux/store'
// 引入actioncreator,专门用于创建action对象
import { createIncrementAction, createDecrementAction} from '../../redux/count_action'
export default class Count extends Component {
increment = () => {
// 普通加
// 1、获取用户选择的值
const { value } = this.selectNumber
// 2、进行计算并更新状态值
// 通知redux更改状态
store.dispatch(createIncrementAction(value * 1))
}
decrement = () => {
// 普通减
// 1、获取用户选择的值
const { value } = this.selectNumber
// 2、进行计算并更新状态值
// 通知redux更改状态
store.dispatch(createDecrementAction(value * 1))
}
incrementIfOdd = () => {
// 当前求和为奇数为
// 1、获取用户选择的值
const { value } = this.selectNumber
// 2、进行计算并更新状态值
if (store.getState() % 2 !== 0) {
// 通知redux更改状态
store.dispatch(createIncrementAction(value * 1))
}
}
incrementAsync = () => {
// 异步加
// 1、获取用户选择的值
const { value } = this.selectNumber
// 2、进行计算并更新状态值
setTimeout(() => {
// 通知redux更改状态
store.dispatch(createIncrementAction(value * 1))
}, 500)
}
render() {
return (
<div>
<h1>当前求和为:{store.getState()}</h1>
<select ref={(c) => (this.selectNumber = c)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数为</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
)
}
}
- 小节总结
新增文件:
(1). count_action.js
:专门为Count
组件服务创建action
对象。
(2). constant.js
:放置容易写错的type
值。
4、Redux中开启异步action
在Redux
中,同步Action
和异步Action
的主要区别在于它们如何触发dispatch
。
同步Action
: 当一个同步Action
被创建时,它会立即被发送到store
中,并等待store
更新状态。如果有其他组件正在使用store
,那么这些组件也会立即更新状态。因此,同步Action
通常用于那些不需要等待的、简单的操作。
异步Action
: 异步Action
会在发出后继续执行其他操作,直到完成后再将结果发送到store
中。这意味着,如果有其他组件正在使用store
,那么它们可能不会立即看到更新的状态。因此,异步Action
通常用于那些需要等待的操作。
- 步骤1:编写一个异步的action的函数
普通的action返回的是一个Object的一般对象,而异步action返回的是一个函数,因为只有在函数里面才能进行异步操作。
文件:redux/count_action.js
// 该文件专门为Count组件生成action对象
import {INCREMENT,DECREMENT} from './constant'
// 一般action,返回的是一般对象
export const createIncrementAction = data => ({ type: INCREMENT,data})
export const createDecrementAction = data => ({ type: DECREMENT,data})
// 异步action,返回的是一个函数
export const createIncrementAsyncAction = (data,time) => {
// 在返回的函数的参数是一个dispatch,用于我们处理完异步任务后分发action对象
return (dispatch) => {
setTimeout(() => {
dispatch(createIncrementAction(data))
}, time);
}
}
- 步骤2:引入中间件来支持异步action
由于store分发action的时候,它只认普通对象,如果要接收一个函数就需要使用到一个中间件。
(1). 安装依赖redux-thunk
npm i redux-thunk
(2). 修改redux/store.js
文件
// 引入createStore,专门用于创建redux中最为核心的store对象
import { createStore , applyMiddleware} from 'redux'
import countReducer from './count_reducer'
// 引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
export default createStore(countReducer,applyMiddleware(thunk))
- 步骤3:在Count组件里面使用异步action
import React, { Component } from 'react'
import store from '../../redux/store'
// 引入actioncreator,专门用于创建action对象
import { createIncrementAsyncAction} from '../../redux/count_action'
export default class Count extends Component {
// ...
incrementAsync = () => {
// 异步加
// 1、获取用户选择的值
const { value } = this.selectNumber
// 2、进行计算并更新状态值
store.dispatch(createIncrementAsyncAction(value*1,500))
}
render() {
return (
<div>
<h1>当前求和为:{store.getState()}</h1>
<select ref={(c) => (this.selectNumber = c)}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数为</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
)
}
}
-
小节总结
(1).明确:延迟的动作不想交给组件自身,想交给
action
(2).何时需要异步
action
:想要对状态进行操作,但是具体的数据靠异步任务返回(3).具体编码:
1). npm i redux-thunk
,并配置在store
中
2). 创建action
的函数不再返回一般对象,而是一个函数,该函数中写异步任务
3). 异步任务有结果后,分发一个同步的action
去真正操作数据。
4). 备注:异步任务action
不是必须要写的,完全可以自己等待异步任务的结果完了,再去分发同步action
。