状态管理
概念
状态管理是指如何高效地管理和共享组件中的状态。React 提供了 useState
和 useReducer
来管理本地状态,而对于全局状态,可以使用 Context API
或第三方库(如 Redux)。
用法
- 本地状态:使用
useState
或useReducer
。 - 全局状态:使用
Context API
或 Redux。
使用场景
- 本地状态:组件内部的状态,如表单输入、按钮点击。
- 全局状态:需要跨组件共享的状态,如用户登录信息、主题设置。
代码示例
// 1. 使用 Context API 管理全局状态
const ThemeContext = React.createContext('light');
function App() {
const [theme, setTheme] = React.useState('light');
const toggleTheme = () => {
setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
<ThemeContext.Provider value={theme}>
<Toolbar toggleTheme={toggleTheme} />
</ThemeContext.Provider>
);
}
function Toolbar({ toggleTheme }) {
return (
<div>
<ThemedButton toggleTheme={toggleTheme} />
</div>
);
}
function ThemedButton({ toggleTheme }) {
const theme = React.useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
style={{ background: theme === 'dark' ? '#333' : '#fff', color: theme === 'dark' ? '#fff' : '#333' }}
>
Toggle Theme
</button>
);
}
代码解析:
ThemeContext.Provider
:提供全局状态theme
。useContext
:在子组件中获取theme
状态。toggleTheme
:通过父组件传递回调函数来更新状态。
下面使用一个功能来说明状态管理的用法
实现功能
- 商品列表:显示用户可以购买的商品。
- 购物车:显示用户已添加的商品,并计算总价。
- 添加商品:用户点击商品时将其添加到购物车。
- 移除商品:用户可以点击按钮从购物车中移除商品。
代码实现
1. 创建 CartContext
管理全局状态
// src/contexts/CartContext.js
import React, { createContext, useReducer, useContext } from 'react';
// 创建 Context
const CartContext = createContext();
// 定义 initialState 初始状态
const initialState = {
cartItems: [], // 购物车中的商品
};
// 定义 Reducer 函数,用于处理状态更新
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
// 检查商品是否已经存在于购物车中
const existingItem = state.cartItems.find(item => item.id === action.payload.id);
if (existingItem) {
// 如果存在,更新数量
return {
...state,
cartItems: state.cartItems.map(item =>
item.id === action.payload.id ? { ...item, quantity: item.quantity + 1 } : item
),
};
} else {
// 如果不存在,添加新商品
return {
...state,
cartItems: [...state.cartItems, { ...action.payload, quantity: 1 }],
};
}
case 'REMOVE_ITEM':
// 移除商品
return {
...state,
cartItems: state.cartItems.filter(item => item.id !== action.payload.id),
};
default:
return state;
}
}
// 创建 Provider 组件,用于包裹应用并提供全局状态
export function CartProvider({ children }) {
const [state, dispatch] = useReducer(cartReducer, initialState); // 使用 useReducer 管理状态
return (
<CartContext.Provider value={{ state, dispatch }}>
{
2. 创建商品列表组件
// src/components/ProductList.js
import React from 'react';
import { useCart } from '../contexts/CartContext'; // 引入 useCart Hook
const products = [
{ id: 1, name: '商品 1', price: 10 },
{ id: 2, name: '商品 2', price: 20 },
{ id: 3, name: '商品 3', price: 30 },
];
function ProductList() {
const { dispatch } = useCart(); // 获取 dispatch 函数
const handleAddToCart = product => {
dispatch({ type: 'ADD_ITEM', payload: product }); // 触发 ADD_ITEM 动作
};
return (
<div>
<h2>商品列表</h2>
<ul>
{products.map(product => (
<li key={product.id}>
{product.name} - ¥{product.price}
<button onClick={() => handleAddToCart(product)}>添加到购物车</button>
</li>
))}
</ul>
</div>
);
}
export default ProductList;
3. 创建购物车组件
// src/components/Cart.js
import React from 'react';
import { useCart } from '../contexts/CartContext'; // 引入 useCart Hook
function Cart() {
const { state, dispatch } = useCart(); // 获取 state 和 dispatch
const handleRemoveFromCart = item => {
dispatch({ type: 'REMOVE_ITEM', payload: item }); // 触发 REMOVE_ITEM 动作
};
// 计算购物车总价
const totalPrice = state.cartItems.reduce((total, item) => total + item.price * item.quantity, 0);
return (
<div>
<h2>购物车</h2>
{state.cartItems.length === 0 ? ( // 如果购物车为空,显示提示信息
<p>购物车为空</p>
) : (
<ul>
{state.cartItems.map(item => (
<li key={item.id}>
{item.name} - ¥{item.price} x {item.quantity}
<button onClick={() => handleRemoveFromCart(item)}>移除</button>
</li>
))}
</ul>
)}
<h3>总价: ¥{totalPrice}</h3>
</div>
);
}
export default Cart;
4. 在主应用中使用 CartProvider
包裹组件
// src/App.js
import React from 'react';
import { CartProvider } from './contexts/CartContext'; // 引入 CartProvider
import ProductList from './components/ProductList'; // 引入商品列表组件
import Cart from './components/Cart'; // 引入购物车组件
function App() {
return (
<CartProvider> // 使用 CartProvider 包裹应用,提供全局状态
<div>
<h1>购物车应用</h1>
<ProductList />
<Cart />
</div>
</CartProvider>
);
}
export default App;
代码解析
CartContext
:- 使用
createContext
创建上下文。 - 使用
useReducer
管理状态,定义ADD_ITEM
和REMOVE_ITEM
两种动作。 CartProvider
包裹整个应用,提供全局状态。
- 使用
ProductList
:- 显示商品列表,点击按钮将商品添加到购物车。
- 使用
dispatch
触发ADD_ITEM
动作。
Cart
:- 显示购物车中的商品和总价。
- 使用
dispatch
触发REMOVE_ITEM
动作。
App
:- 使用
CartProvider
包裹应用,使全局状态在ProductList
和Cart
之间共享。
- 使用
运行效果
- 用户可以从商品列表中添加商品到购物车。
- 购物车会显示已添加的商品和总价。
- 用户可以点击“移除”按钮从购物车中删除商品。
总结
通过这个例子,我们学习了如何使用 Context API
和 useReducer
管理全局状态:
- 状态管理:将购物车的状态和逻辑集中管理。
- 组件复用:商品列表和购物车组件各自独立,但共享相同的状态。
- 代码结构清晰:将上下文、组件和主应用分离,易于维护和扩展。
通过模块化的方式进行管理实现
实现思路
state
:单独定义全局状态。actions
:定义操作状态的函数。reducer
:处理状态更新逻辑。store
:创建 Redux store,集中管理状态。- 组件:在其他组件中通过
useSelector
和useDispatch
访问状态和触发动作。
代码实现
1. 定义 state
、actions
和 reducer
// src/store/cartSlice.js
import { createSlice } from '@reduxjs/toolkit'; // 使用 Redux Toolkit 简化代码
// 定义初始状态
const initialState = {
cartItems: [], // 购物车中的商品
};
// 创建 slice,包含 reducer 和 actions
const cartSlice = createSlice({
name: 'cart', // slice 名称
initialState, // 初始状态
reducers: {
// 添加商品的 reducer
addItem: (state, action) => {
const existingItem = state.cartItems.find(item => item.id === action.payload.id);
if (existingItem) {
existingItem.quantity += 1; // 如果商品已存在,增加数量
} else {
state.cartItems.push({ ...action.payload, quantity: 1 }); // 如果不存在,添加新商品
}
},
// 移除商品的 reducer
removeItem: (state, action) => {
state.cartItems = state.cartItems.filter(item => item.id !== action.payload.id);
},
},
});
// 导出 actions
export const { addItem, removeItem } = cartSlice.actions;
// 导出 reducer
export default cartSlice.reducer;
2. 创建 store
,集中管理状态
// src/store/store.js
import { configureStore } from '@reduxjs/toolkit'; // 使用 Redux Toolkit 简化代码
import cartReducer from './cartSlice'; // 引入 cartReducer
// 创建 store
const store = configureStore({
reducer: {
cart: cartReducer, // 将 cartReducer 挂载到 store
},
});
export default store;
3. 在主应用中使用 Provider
包裹组件
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'; // 引入 Provider
import store from './store/store'; // 引入 store
import App from './App';
ReactDOM.render(
<Provider store={store}> {/* 使用 Provider 包裹应用,传递 store */}
<App />
</Provider>,
document.getElementById('root')
);
4. 创建商品列表组件
// src/components/ProductList.js
import React from 'react';
import { useDispatch } from 'react-redux'; // 引入 useDispatch
import { addItem } from '../store/cartSlice'; // 引入 addItem action
const products = [
{ id: 1, name: '商品 1', price: 10 },
{ id: 2, name: '商品 2', price: 20 },
{ id: 3, name: '商品 3', price: 30 },
];
function ProductList() {
const dispatch = useDispatch(); // 获取 dispatch 函数
const handleAddToCart = product => {
dispatch(addItem(product)); // 触发 addItem action
};
return (
<div>
<h2>商品列表</h2>
<ul>
{products.map(product => (
<li key={product.id}>
{product.name} - ¥{product.price}
<button onClick={() => handleAddToCart(product)}>添加到购物车</button>
</li>
))}
</ul>
</div>
);
}
export default ProductList;
5. 创建购物车组件
// src/components/Cart.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux'; // 引入 useSelector 和 useDispatch
import { removeItem } from '../store/cartSlice'; // 引入 removeItem action
function Cart() {
const cartItems = useSelector(state => state.cart.cartItems); // 获取购物车商品
const dispatch = useDispatch(); // 获取 dispatch 函数
const handleRemoveFromCart = item => {
dispatch(removeItem(item)); // 触发 removeItem action
};
// 计算购物车总价
const totalPrice = cartItems.reduce((total, item) => total + item.price * item.quantity, 0);
return (
<div>
<h2>购物车</h2>
{cartItems.length === 0 ? ( // 如果购物车为空,显示提示信息
<p>购物车为空</p>
) : (
<ul>
{cartItems.map(item => (
<li key={item.id}>
{item.name} - ¥{item.price} x {item.quantity}
<button onClick={() => handleRemoveFromCart(item)}>移除</button>
</li>
))}
</ul>
)}
<h3>总价: ¥{totalPrice}</h3>
</div>
);
}
export default Cart;
6. 主应用组件
// src/App.js
import React from 'react';
import ProductList from './components/ProductList'; // 引入商品列表组件
import Cart from './components/Cart'; // 引入购物车组件
function App() {
return (
<div>
<h1>购物车应用</h1>
<ProductList />
<Cart />
</div>
);
}
export default App;
在这个例子中,Redux 中公共管理的状态是定义在 cartSlice.js
中的 initialState
,具体是:
const initialState = { cartItems: [], // 购物车中的商品};
cartItems
:- 这是一个数组,用于存储用户添加到购物车中的商品。
- 每件商品是一个对象,包含以下属性:
id
:商品的唯一标识。name
:商品名称。price
:商品价格。quantity
:商品的数量。
全局状态:
- 在 Redux 中,
initialState
是这个 slice(模块)的初始状态。 - 这个状态通过
store.js
挂载到 Redux 的全局状态树中。 - 其他组件可以通过
useSelector
访问这个状态,也可以通过useDispatch
触发动作来修改这个状态。
- 在 Redux 中,
代码解析
cartSlice.js
:- 使用 Redux Toolkit 的
createSlice
定义state
、reducer
和actions
。 addItem
:添加商品到购物车,如果商品已存在则增加数量。removeItem
:从购物车中移除商品。
- 使用 Redux Toolkit 的
store.js
:- 使用
configureStore
创建 Redux store。 - 将
cartSlice
的reducer
挂载到 store。
- 使用
ProductList.js
:- 使用
useDispatch
触发addItem
动作,将商品添加到购物车。
- 使用
Cart.js
:- 使用
useSelector
获取购物车中的商品。 - 使用
useDispatch
触发removeItem
动作,移除商品。
- 使用
App.js
:- 展示商品列表和购物车组件。
总结
通过这种方式,我们实现了类似 Vuex 的状态管理结构:
state
和actions
单独抽离,模块化管理。- 使用 Redux Toolkit 简化了 Redux 的代码。
- 在组件中通过
useSelector
和useDispatch
访问和操作状态。
这种模式非常适合大型应用的状态管理,代码结构清晰,易于维护和扩展。 😊