Redux快速学习

Date Published

Redux 的核心概念

1. Redux 是什么?

Redux 是一个可预测的状态管理库,主要用于管理应用的状态。它的核心思想是:

单一数据源:整个应用的状态存储在一个对象中

状态是只读的:只能通过 dispatch action 来修改状态

使用纯函数进行修改:reducer 必须是纯函数

2. Redux 的三大原则

```

// 1. 单一数据源
const store = createStore(reducer)
// 整个应用只有一个 store
// 2. 状态是只读的
// ❌ 错误:直接修改状态
store.getState().user.name = 'John'

// ✅ 正确:通过 dispatch action 修改
store.dispatch({ type: 'UPDATE_USER_NAME', payload: 'John' })

// 3. 使用纯函数进行修改
function reducer(state, action) {
switch (action.type) {
case 'UPDATE_USER_NAME':
return {
...state,
user: {
...state.user,
name: action.payload
}
}
default:
return state
}
}

```

Redux 的基本使用

1. 创建 Store

import { createStore } from 'redux'

// 初始状态
const initialState = {
user: null,
posts: [],
loading: false,
error: null
}

// Reducer
function appReducer(state = initialState, action) {
switch (action.type) {
case 'SET_USER':
return {
...state,
user: action.payload
}

case 'SET_POSTS':
return {
...state,
posts: action.payload
}

case 'SET_LOADING':
return {
...state,
loading: action.payload
}

case 'SET_ERROR':
return {
...state,
error: action.payload
}

default:
return state
}
}

// 创建 store
const store = createStore(appReducer)

export default store

2. Action 和 Action Creator

// Action - 描述发生了什么
const action = {
type: 'ADD_TODO',
payload: {
id: 1,
text: 'Learn Redux',
completed: false
}
}

// Action Creator - 创建 action 的函数
const addTodo = (text) => ({
type: 'ADD_TODO',
payload: {
id: Date.now(),
text,
completed: false
}
})

const setUser = (user) => ({
type: 'SET_USER',
payload: user
})

const setLoading = (loading) => ({
type: 'SET_LOADING',
payload: loading
})

// 异步 Action Creator
const fetchUser = (userId) => {
return async (dispatch) => {
dispatch(setLoading(true))

try {
const response = await fetch(`/api/users/${userId}`)
const user = await response.json()
dispatch(setUser(user))
} catch (error) {
dispatch({ type: 'SET_ERROR', payload: error.message })
} finally {
dispatch(setLoading(false))
}
}
}

3. 在 React 中使用 Redux

import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { addTodo, fetchUser } from './actions'

function TodoList() {
// 获取状态
const todos = useSelector(state => state.todos)
const loading = useSelector(state => state.loading)
const user = useSelector(state => state.user)

// 获取 dispatch 函数
const dispatch = useDispatch()

const handleAddTodo = (text) => {
dispatch(addTodo(text))
}

const handleFetchUser = (userId) => {
dispatch(fetchUser(userId))
}

return (
<div>
{loading && <div>Loading...</div>}

{user && <div>Welcome, {user.name}!</div>}

<button onClick={() => handleFetchUser(1)}>
Fetch User
</button>

<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>

<button onClick={() => handleAddTodo('New Todo')}>
Add Todo
</button>
</div>
)
}

Redux 的高级特性

1. 组合 Reducers

import { combineReducers } from 'redux'

// 用户相关的 reducer
function userReducer(state = null, action) {
switch (action.type) {
case 'SET_USER':
return action.payload
case 'UPDATE_USER':
return { ...state, ...action.payload }
case 'CLEAR_USER':
return null
default:
return state
}
}

// 文章相关的 reducer
function postsReducer(state = [], action) {
switch (action.type) {
case 'SET_POSTS':
return action.payload
case 'ADD_POST':
return [...state, action.payload]
case 'UPDATE_POST':
return state.map(post =>
post.id === action.payload.id
? { ...post, ...action.payload }
: post
)
case 'DELETE_POST':
return state.filter(post => post.id !== action.payload)
default:
return state
}
}

// UI 状态相关的 reducer
function uiReducer(state = { loading: false, error: null }, action) {
switch (action.type) {
case 'SET_LOADING':
return { ...state, loading: action.payload }
case 'SET_ERROR':
return { ...state, error: action.payload }
case 'CLEAR_ERROR':
return { ...state, error: null }
default:
return state
}
}

// 组合所有 reducers
const rootReducer = combineReducers({
user: userReducer,
posts: postsReducer,
ui: uiReducer
})

const store = createStore(rootReducer)

2. 中间件(Middleware)

import { applyMiddleware, createStore } from 'redux'
import thunk from 'redux-thunk'
import logger from 'redux-logger'

// 自定义中间件
const customMiddleware = store => next => action => {
console.log('Dispatching:', action)

// 在 action 被处理之前做一些事情
if (action.type === 'SET_USER') {
// 保存用户信息到 localStorage
localStorage.setItem('user', JSON.stringify(action.payload))
}

// 调用下一个中间件或 reducer
const result = next(action)

// 在 action 被处理之后做一些事情
console.log('New State:', store.getState())

return result
}

// 应用中间件
const store = createStore(
rootReducer,
applyMiddleware(thunk, logger, customMiddleware)
)

3. Redux Toolkit(现代 Redux)

import { createSlice, configureStore } from '@reduxjs/toolkit'

// 使用 createSlice 创建 reducer
const userSlice = createSlice({
name: 'user',
initialState: null,
reducers: {
setUser: (state, action) => action.payload,
updateUser: (state, action) => ({ ...state, ...action.payload }),
clearUser: () => null
}
})

const postsSlice = createSlice({
name: 'posts',
initialState: [],
reducers: {
setPosts: (state, action) => action.payload,
addPost: (state, action) => {
state.push(action.payload) // 可以直接修改 state,Immer 会处理不可变性
},
updatePost: (state, action) => {
const index = state.findIndex(post => post.id === action.payload.id)
if (index !== -1) {
state[index] = { ...state[index], ...action.payload }
}
},
deletePost: (state, action) => {
return state.filter(post => post.id !== action.payload)
}
}
})

// 异步 thunk
export const fetchUser = createAsyncThunk(
'user/fetchUser',
async (userId) => {
const response = await fetch(`/api/users/${userId}`)
return response.json()
}
)

// 处理异步 action
const userSlice = createSlice({
name: 'user',
initialState: { data: null, loading: false, error: null },
reducers: {
// 同步 reducers...
},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true
state.error = null
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false
state.data = action.payload
})
.addCase(fetchUser.rejected, (state, action) => {
state.loading = false
state.error = action.error.message
})
}
})

// 创建 store
const store = configureStore({
reducer: {
user: userSlice.reducer,
posts: postsSlice.reducer
}
})

export const { setUser, updateUser, clearUser } = userSlice.actions
export const { setPosts, addPost, updatePost, deletePost } = postsSlice.actions

Redux 的实际应用场景

1. 购物车功能

// 购物车 slice
const cartSlice = createSlice({
name: 'cart',
initialState: {
items: [],
total: 0
},
reducers: {
addToCart: (state, action) => {
const { id, name, price } = action.payload
const existingItem = state.items.find(item => item.id === id)

if (existingItem) {
existingItem.quantity += 1
} else {
state.items.push({ id, name, price, quantity: 1 })
}

state.total = state.items.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
)
},

removeFromCart: (state, action) => {
const id = action.payload
state.items = state.items.filter(item => item.id !== id)
state.total = state.items.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
)
},

updateQuantity: (state, action) => {
const { id, quantity } = action.payload
const item = state.items.find(item => item.id === id)
if (item) {
item.quantity = quantity
state.total = state.items.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
)
}
}
}
})

// 购物车组件
function ShoppingCart() {
const { items, total } = useSelector(state => state.cart)
const dispatch = useDispatch()

const handleAddToCart = (product) => {
dispatch(addToCart(product))
}

const handleRemoveFromCart = (productId) => {
dispatch(removeFromCart(productId))
}

const handleUpdateQuantity = (productId, quantity) => {
dispatch(updateQuantity({ id: productId, quantity }))
}

return (
<div>
<h2>购物车 ({items.length})</h2>
<p>总计: ¥{total}</p>

{items.map(item => (
<div key={item.id}>
<span>{item.name}</span>
<span>数量: {item.quantity}</span>
<span>¥{item.price * item.quantity}</span>
<button onClick={() => handleRemoveFromCart(item.id)}>
删除
</button>
<input
type="number"
value={item.quantity}
onChange={(e) => handleUpdateQuantity(item.id, parseInt(e.target.value))}
/>
</div>
))}
</div>
)
}

2. 用户认证状态管理

const authSlice = createSlice({
name: 'auth',
initialState: {
user: null,
token: localStorage.getItem('token'),
isAuthenticated: !!localStorage.getItem('token'),
loading: false,
error: null
},
reducers: {
loginStart: (state) => {
state.loading = true
state.error = null
},
loginSuccess: (state, action) => {
state.loading = false
state.user = action.payload.user
state.token = action.payload.token
state.isAuthenticated = true
localStorage.setItem('token', action.payload.token)
},
loginFailure: (state, action) => {
state.loading = false
state.error = action.payload
},
logout: (state) => {
state.user = null
state.token = null
state.isAuthenticated = false
localStorage.removeItem('token')
}
}
})

// 异步登录 action
export const login = createAsyncThunk(
'auth/login',
async (credentials) => {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
})

if (!response.ok) {
throw new Error('Login failed')
}

return response.json()
}
)

// 登录组件
function LoginForm() {
const { loading, error, isAuthenticated } = useSelector(state => state.auth)
const dispatch = useDispatch()
const [credentials, setCredentials] = useState({ email: '', password: '' })

const handleSubmit = async (e) => {
e.preventDefault()
dispatch(login(credentials))
}

if (isAuthenticated) {
return <Navigate to="/dashboard" />
}

return (
<form onSubmit={handleSubmit}>
{error && <div className="error">{error}</div>}

<input
type="email"
placeholder="Email"
value={credentials.email}
onChange={(e) => setCredentials({ ...credentials, email: e.target.value })}
/>

<input
type="password"
placeholder="Password"
value={credentials.password}
onChange={(e) => setCredentials({ ...credentials, password: e.target.value })}
/>

<button type="submit" disabled={loading}>
{loading ? '登录中...' : '登录'}
</button>
</form>
)
}

Redux 的优缺点

优点:

可预测性:状态变化可追踪

集中管理:所有状态在一个地方

调试友好:Redux DevTools 支持

生态系统丰富:大量中间件和工具

缺点:

样板代码多:传统 Redux 需要写很多代码

学习曲线陡峭:概念较多

小项目过重:简单项目可能过度设计

异步处理复杂:需要中间件处理异步

总结

Redux 是一个强大的状态管理库,特别适合:

大型应用:需要复杂状态管理

团队协作:需要统一的状态管理模式

调试需求:需要详细的状态变化追踪

对于现代 React 开发,推荐使用 Redux Toolkit,它大大简化了 Redux 的使用,减少了样板代码,提供了更好的开发体验。