🚀 Redux使用与源码解析
📚 Redux基础使用
1. 基本概念 🌟
Redux是一个可预测的状态容器,它遵循三大原则:
- 单一数据源
- 状态是只读的
- 使用纯函数进行修改
2. 基础使用示例
js
import { createStore } from 'redux';
// 1️⃣ 初始状态
const initial = {
name: "chenyq",
age: 18
}
// 2️⃣ Reducer函数:处理状态更新的纯函数
const reducer = function reducer(state = initial, action) {
// 创建新的状态对象,避免直接修改
let newState = {...state}
switch(action.type){
case 'changeName':
newState.name = 'xxx';
break;
case 'changeAge':
newState.age = 16;
break;
default:
// 默认返回原状态
return state;
}
return newState;
}
// 3️⃣ 创建Store
const store = createStore(reducer);
export default store;
// 4️⃣ 使用Store
import store from '@/store'
// 获取状态
let { name, age } = store.getState()
// 修改状态
store.dispatch({ type: "change_name", name: "abc" })3. 组件更新控制 🔄
js
// 方法一:通过数字自增控制更新
const [num, forceUpdate] = useState(0)
useEffect(() => {
let unsubscribe = store.subscribe(() => {
forceUpdate(num + 1)
})
return () => {
unsubscribe() // 清理订阅
}
}, [num])
// 方法二:使用时间戳控制更新
const [_, forceUpdate] = useState(0)
useEffect(() => {
store.subscribe(() => {
forceUpdate(+new Date())
})
}, [])
// 方法三:类组件更新方式
componentDidMount() {
const { store } = this.context;
store.subscribe(() => {
this.forceUpdate();
});
}🌈 Redux工程化实践
1. 目录结构 📁
src/
├── store/
│ ├── reducers/
│ │ ├── index.js # reducer合并
│ │ └── xxxReducer.js # 具体模块reducer
│ ├── actions/
│ │ ├── index.js # action合并
│ │ └── xxxAction.js # 具体模块action
│ ├── index.js # store配置
│ └── action-types.js # action类型常量2. Reducer模块化 🔨
Reducer模板
js
import _ from '@/assets/utils'
import * as TYPES from '../action-types'
const initial = {}
export default function xxxReducer(state = initial, action) {
// 深克隆确保状态不可变性
state = _.clone(true, state)
switch(action.type) {
case TYPES.VOTE_OPP:
state.xxx = action.xxx
break;
case TYPES.VOTE_SUP:
state.xxx = action.xxx
break;
default:
}
return state
}合并Reducer
js
import { combineReducers } from 'redux'
import xxxReducer from './xxxReducer'
// 使用combineReducers合并多个reducer
const reducer = combineReducers({
xxx: xxxReducer,
// ... 其他reducer
})
export default reducer3. Action管理 📝
action-types.js
js
/*
* 统一管理action类型
* 命名规范:模块名_动作(大写)
*/
export const VOTE_SUP = 'VOTE_SUP'
export const VOTE_OPP = 'VOTE_OPP'
export const MAIN_SUP = 'MAIN_SUP'
export const MAIN_OPP = 'MAIN_OPP'actionCreator
js
// actions/voteAction.js
import * as TYPES from '../action-types'
const voteAction = {
support() {
return {
type: TYPES.VOTE_SUP
}
},
oppose() {
return {
type: TYPES.VOTE_OPP
}
}
}
export default voteAction🛠️ Redux中间件
1. redux-logger 📊
js
import { createStore, applyMiddleware } from 'redux';
import reducer from './reducers';
import reduxLogger from 'redux-logger';
const store = createStore(
reducer,
applyMiddleware(reduxLogger)
);2. 异步Action处理 ⚡
redux-promise
js
const action = {
async support() {
await delay()
return {
type: TYPES.VOTE_SUP
}
}
}redux-thunk
js
const action = {
support() {
/*
* redux-thunk工作原理:
* 1. 返回函数而不是对象
* 2. 函数接收dispatch作为参数
* 3. 在异步操作完成后手动dispatch
*/
return async(dispatch) => {
await delay()
dispatch({ type: TYPES.VOTE_SUP })
}
}
}🎯 Redux Toolkit(RTK)
1. 基本配置
js
import { configureStore } from '@reduxjs/toolkit'
import reduxLogger from 'redux-logger'
import taskSliceReducer from './features/taskSlice'
const store = configureStore({
// 指定reducer
reducer: {
task: taskSliceReducer
},
// 配置中间件
middleware: [reduxLogger]
})2. Slice示例
js
import { createSlice } from '@reduxjs/toolkit'
const taskSlice = createSlice({
name: 'task',
initialState: {
taskList: null
},
reducers: {
getAllTaskList(state, action) {
// RTK内部使用Immer,可以"直接修改"状态
state.taskList = action.payload
}
}
})💡 最佳实践
- 使用常量定义action类型
- 遵循不可变性原则
- 保持reducer纯函数特性
- 合理拆分reducer模块