给大家详细讲一下React中的Redux,最近收到不少同学想让我说一说Redux,要是有不明白的地方,很欢迎大家私信来一起交流!!
1. Redux 核心概念
1. Store:存储状态的容器
2.Action:描述发生了什么的普通对象
3.Reducer:定义状态如何更新的纯函数
4Dispatch:发送 action 的方法
5.Subscribe:订阅 store 变化的方法
2. 基础设置
// 1. 安装依赖
npm install redux react-redux @reduxjs/toolkit
// 2. 创建 store 目录结构
src/
store/
index.js // store 配置
reducers/ // reducer 文件夹
actions/ // action 文件夹
types/ // action 类型常量
3. 创建 Store
// store/index.js
import { configureStore } from '@reduxjs/toolkit'
import rootReducer from './reducers'
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(logger),
devTools: process.env.NODE_ENV !== 'production'
})
export default store
4. 定义 Action Types
// store/types/userTypes.js
export const USER_TYPES = {
LOGIN_REQUEST: 'user/loginRequest',
LOGIN_SUCCESS: 'user/loginSuccess',
LOGIN_FAILURE: 'user/loginFailure',
LOGOUT: 'user/logout'
}
// store/types/todoTypes.js
export const TODO_TYPES = {
ADD_TODO: 'todo/add',
REMOVE_TODO: 'todo/remove',
TOGGLE_TODO: 'todo/toggle'
}
5. 创建 Action Creators
// store/actions/userActions.js
import { USER_TYPES } from '../types/userTypes'
export const userActions = {
loginRequest: (credentials) => ({
type: USER_TYPES.LOGIN_REQUEST,
payload: credentials
}),
loginSuccess: (user) => ({
type: USER_TYPES.LOGIN_SUCCESS,
payload: user
}),
loginFailure: (error) => ({
type: USER_TYPES.LOGIN_FAILURE,
payload: error
}),
logout: () => ({
type: USER_TYPES.LOGOUT
})
}
// 异步 Action Creator
export const login = (credentials) => {
return async (dispatch) => {
dispatch(userActions.loginRequest(credentials))
try {
const response = await api.login(credentials)
dispatch(userActions.loginSuccess(response.data))
} catch (error) {
dispatch(userActions.loginFailure(error.message))
}
}
}
6. 创建 Reducers
// store/reducers/userReducer.js
import { USER_TYPES } from '../types/userTypes'
const initialState = {
user: null,
loading: false,
error: null
}
export const userReducer = (state = initialState, action) => {
switch (action.type) {
case USER_TYPES.LOGIN_REQUEST:
return {
...state,
loading: true,
error: null
}
case USER_TYPES.LOGIN_SUCCESS:
return {
...state,
loading: false,
user: action.payload
}
case USER_TYPES.LOGIN_FAILURE:
return {
...state,
loading: false,
error: action.payload
}
case USER_TYPES.LOGOUT:
return initialState
default:
return state
}
}
7. 合并 Reducers
// store/reducers/index.js
import { combineReducers } from 'redux'
import { userReducer } from './userReducer'
import { todoReducer } from './todoReducer'
const rootReducer = combineReducers({
user: userReducer,
todos: todoReducer
})
export default rootReducer
8. 在 React 中使用
// index.js
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
// 组件中使用
import { useSelector, useDispatch } from 'react-redux'
import { userActions } from '../store/actions/userActions'
const UserComponent = () => {
const dispatch = useDispatch()
const { user, loading, error } = useSelector(state => state.user)
const handleLogin = (credentials) => {
dispatch(userActions.login(credentials))
}
const handleLogout = () => {
dispatch(userActions.logout())
}
return (
<div>
{loading && <LoadingSpinner />}
{error && <ErrorMessage error={error} />}
{user ? (
<UserProfile user={user} onLogout={handleLogout} />
) : (
<LoginForm onSubmit={handleLogin} />
)}
</div>
)
}
9. Redux Toolkit 使用(推荐)
// store/slices/userSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
// 创建异步 action
export const loginAsync = createAsyncThunk(
'user/login',
async (credentials, { rejectWithValue }) => {
try {
const response = await api.login(credentials)
return response.data
} catch (error) {
return rejectWithValue(error.message)
}
}
)
const userSlice = createSlice({
name: 'user',
initialState: {
user: null,
loading: false,
error: null
},
reducers: {
logout: (state) => {
state.user = null
state.error = null
}
},
extraReducers: (builder) => {
builder
.addCase(loginAsync.pending, (state) => {
state.loading = true
state.error = null
})
.addCase(loginAsync.fulfilled, (state, action) => {
state.loading = false
state.user = action.payload
})
.addCase(loginAsync.rejected, (state, action) => {
state.loading = false
state.error = action.payload
})
}
})
export const { logout } = userSlice.actions
export default userSlice.reducer
10. 中间件使用
// store/middleware/loggerMiddleware.js
export const loggerMiddleware = (store) => (next) => (action) => {
console.log('dispatching', action)
const result = next(action)
console.log('next state', store.getState())
return result
}
// store/index.js
import { configureStore } from '@reduxjs/toolkit'
import { loggerMiddleware } from './middleware/loggerMiddleware'
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(loggerMiddleware)
})
11. 最佳实践
1.使用 Redux Toolkit
- 简化配置
- 内置 immer 处理不可变更新
- 自动配置 Redux DevTools
- 包含常用中间件
2.组织结构
- 按功能模块划分
- 使用 ducks 模式或 feature 文件夹
3. 性能优化
- 使用 reselect 做选择器
- 避免不必要的渲染
- 合理拆分 state
4 . 异步处理
- 使用 createAsyncThunk
- 处理加载状态和错误状态
- 合理使用缓存
这就是 Redux 的主要使用方法和最佳实践。记住:
- 保持 reducer 纯函数
- 不要直接修改 state
- 合理组织 action 和 reducer
- 使用 Redux Toolkit 简化开发