Redux是一个帮助我们管理state的容器:Redux是javascript的状态容器,提供了可预测的状态管理
在vue2中使用的是vuex,vue3中使用的是pinia
而在react中使用的是redux进行状态管理或是RTK(redux-toolkit),在现在当然也有一些其他的状态管理 zustand,easypeasy或是使用vite来创建react的项目
但是一些老的react16、17的项目或是之前的都普遍用的是redux或是RTK来进行状态管理
redux可以完全脱离react在其他的项目中运行,比如uniapp等,并且包也是非常的小,加上依赖一共才2kb
Redux 三大原则
- 单一数据源:整个应用状态存储在单个 store 中
- 状态只读:只能通过 action 修改状态
- 纯函数修改:使用纯函数 reducer 执行状态更新
tsx
// 伪代码结构
{
store: {
state, // 存储应用状态
dispatch, // 触发 action 的方法
subscribe, // 监听状态变化
getState // 获取当前状态
},
action: { type, payload }, // 描述状态变化的对象
reducer: (state, action) => newState // 状态处理函数
}
来个计数的小例子
创建action
jsx
// actionCreator.jsx
import { INCREMENT, DECREMENT } from './constants.jsx';
export const increment = (num) => {
return {
type: INCREMENT,
payload: num
};
};
export const decrement = (num) => {
return {
type: DECREMENT,
payload: num
};
};
常量参数,类型
jsx
// constants.jsx
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
store实例
jsx
import { createStore } from 'redux';
import reducer from './reducer'
const store = createStore(reducer)
export default store;
reducer函数
jsx
import { INCREMENT, DECREMENT } from './constants.jsx';
// 初始状态
const initialState = {
count: 0
};
// 定义 reducer 函数
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case INCREMENT:
return {
...state,
count: state.count + action.payload
};
case DECREMENT:
return {
...state,
count: state.count +action.payload
};
default:
return state;
}
};
export default counterReducer;
jsx
// app.jsx
import store from './store';
import { increment, decrement } from './store/actionCreator';
const App = () => {
const changeNum = (num) => {
return () => {
if (num > 0) {
store.dispatch(increment(num));
console.log(store.getState().count);
} else {
store.dispatch(decrement(num));
console.log(store.getState().count);
}
};
};
return (
<>
<button onClick={changeNum(1)}>+1</button>
<button onClick={changeNum(5)}>+5</button>
<button onClick={changeNum(-4)}>-4</button>
<button onClick={changeNum(-10)}>-10</button>
</>
);
};
export default App;
最终效果
RTK基本使用
安装
bash
pnpm i @reduxjs/toolkit react-redux
核心api
configureStore: 包装createStore以提供简化的配置选项和良好的默认值,它可以自动组合slice reducer,添加你提供的任何 Redux 中间件,redux-thunk 默认包含 ,并启用 Redux DevTools Extension
createSlice:接受 reducer 函数的对象,切片名称和初始状态,并自动切片 reducer ,并带有相应的 actions
createAsycThunk:接受动作类型字符串和一个返回承诺的函数,并生成一个pending/fulfilled/reject二点基于改承诺分派动作类型的 thunk
项目结构
bash
src/
├── index.js # 应用入口
├── App.js
├── views/
│ ├── Home.js
│ └── Profile.js
└── store/
├── index.js # store 配置
└── modules/
└── counter.js # counter slice
入口文件 main.jsx
jsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import { Provider } from 'react-redux' // redux 连接组件
import store from './store/index.js' // 导入创建好的 store
createRoot(document.getElementById('root')).render(
<StrictMode>
<Provider store={store}> // 必须用 Provider 包裹整个应用提供 store
<App />
</Provider>
</StrictMode>
)
根组件 App.jsx
jsx
import React from 'react'
import Home from './views/home'
import Profile from './views/profile'
import './styles/index.css'
const App = () => {
return (
<div className='pages'>
<Home></Home>
<Profile></Profile>
</div>
)
}
export default App
Home 组件
jsx
import { useSelector, useDispatch } from 'react-redux'
import { increment, decrement } from '../store/modules/counter'
const Home = () => {
// 从 store 获取数据
const count = useSelector(state => state.counter.count)
// 获取 dispatch 方法
const dispatch = useDispatch()
return (
<div className="home">
<h1>Home Page Counter: {count}</h1>
<div className="buttons">
<button onClick={() => dispatch(increment(1))}>+1</button>
<button onClick={() => dispatch(decrement(1))}>-1</button>
</div>
</div>
)
}
export default Home
关键参数说明:
useSelector
:从 store 提取数据的 hookstate.counter.count
:对应 store 的模块结构useDispatch
:获取 dispatch 方法用于触发 actionincrement(1)
:调用 action creator 并传递 payload
Profile组件
jsx
import { useSelector } from 'react-redux'
const Profile = () => {
// 共享同一个 counter 状态
const count = useSelector(state => state.counter.count)
return (
<div className="profile">
<h1>Profile Page Counter: {count}</h1>
<p>This component demonstrates state sharing across components</p>
</div>
)
}
export default Profile
注意 使用数据时 访问的是 state.counter.count 而不是 state.count
因为我们在配置 store 的时候合并了 reducer
jsxconst store = configureStore({ reducer: { counter: counterReducer // 模块化 reducer }, })
后续如果需要添加模块直接在 modules 文件夹下进行新建
store 配置
jsx
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from './modules/counter' // 导入 counter slice
const store = configureStore({
reducer: {
counter: counterReducer // 模块化 reducer
},
// 默认包含以下中间件:
// 1. redux-thunk
// 2. 开发环境下的 Redux DevTools 集成
})
export default store
配置参数说明:
configureStore
自动配置:- 合并多个 reducer
- 集成 Redux Thunk 中间件
- 开发环境自动启用 Redux DevTools
- 模块化结构便于扩展
counter 模块
jsx
import { createSlice } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter', // 命名空间(自动生成 action type 前缀)
initialState: {
count: 0 // 初始状态
},
reducers: {
// 自动生成 action creators
increment: (state, action) => {
// 使用 Immer 库,可以直接修改 state
state.count += action.payload || 1
},
decrement: (state, action) => {
state.count -= action.payload || 1
},
// 可以添加更多 reducer...
}
})
// 导出生成的 action creators
export const { increment, decrement } = counterSlice.actions
// 导出 reducer 供 store 配置使用
export default counterSlice.reducer
配置参数说明
name:切片名称,用于生成 action type 前缀(如 "counter/increment")
initialState:模块的初始状态
reducers:定义 reducer 函数和对应的 action creators
最终实现效果
异步操作
Redux Toolkit 官方推荐使用 createAsyncThunk
+ extraReducers
处理异步流程
yaml
store/
├── modules/
│ ├── pic.js # 新增图片模块
│ └── counter.js # 之前的计数器模块
createAsyncThunk 用于创建一个异步的action
extraReducers 用于处理 异步 action 在执行请求的时候对应状态的处理
js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import { getPic } from '@/apis/pic'
// 创建异步 Thunk(核心异步操作)是一个异步的 action
/* 当 createAsyncThunk 创建出来的 action 被 dispatch 时,会存在三种状态
pending:正在请求数据中结果还未返回
fulfilled: 请求成功
rejected: 请求失败
对应是否请求结果的状态需要在 extraReducers 这个额外的 reducer 里面监听结果
*/
export const fetchPic = createAsyncThunk(
'pic/fetchPic',
/*
第一个参数:调用 fetchPic 时传入的参数(如 fetchPic({ id: 1 }))。
第二个参数:thunkAPI 对象,包含 dispatch、getState、rejectWithValue 等工具。
当请求出现异常情况或是错误时,返回一个自定义错误对象进行精细化管理
*/
async (params, { rejectWithValue }) => {
const response = await getPic()
if (response.success === true) {
return response.data
}
return rejectWithValue({
message: response.message || '图片获取失败',
code: response.code,
})
}
)
const initialState = {
data: null,
status: 'idle', // 异步状态标识('idle' | 'loading' | 'succeeded' | 'failed')
error: null,
}
// 创建 Slice
const picSlice = createSlice({
name: 'pic', // Slice 名称(用于生成 action types)
initialState,
// 同步 Reducers(可选)
reducers: {
resetPic: state => {
state.data = null
state.status = 'idle'
state.error = null
},
},
// 异步 Reducers
/*
builder 是一个对象,它包含了三个方法:addCase、addMatcher 和 addDefaultCase。
addCase 方法用于添加一个异步 action 的处理函数。
addMatcher 方法用于添加一个异步 action 的匹配函数。
addDefaultCase 方法用于添加一个默认的处理函数。
*/
/*
addCase 方法的第一个参数是一个异步 action,第二个参数是一个处理函数。回调里面的;两个参数: state 和 action。
state 是当前的状态,action 是异步 action 的结果。
*/
extraReducers: builder => {
builder
.addCase(fetchPic.pending, state => {
state.status = 'loading'
state.error = null
})
.addCase(fetchPic.fulfilled, (state, action) => {
state.status = 'succeeded'
state.data = action.payload
})
.addCase(fetchPic.rejected, (state, action) => {
state.status = 'failed'
state.error = action.payload
})
},
})
export const { resetPic } = picSlice.actions
export default picSlice.reducer