面试题答案
一键面试1. 使用 useReducer
实现购物车组件状态管理
首先,我们定义购物车状态的结构和 action
的类型。
import React, { useReducer } from'react';
// 定义购物车商品类型
type CartItem = {
id: number;
name: string;
price: number;
quantity: number;
};
// 定义购物车状态类型
type CartState = {
items: CartItem[];
totalPrice: number;
discount: number;
};
// 定义 action 类型
enum CartActionType {
ADD_ITEM = 'ADD_ITEM',
REMOVE_ITEM = 'REMOVE_ITEM',
INCREMENT_QUANTITY = 'INCREMENT_QUANTITY',
DECREMENT_QUANTITY = 'DECREMENT_QUANTITY',
APPLY_DISCOUNT = 'APPLY_DISCOUNT'
}
// 定义 action 接口
type CartAction =
| { type: CartActionType.ADD_ITEM; payload: CartItem }
| { type: CartActionType.REMOVE_ITEM; payload: number }
| { type: CartActionType.INCREMENT_QUANTITY; payload: number }
| { type: CartActionType.DECREMENT_QUANTITY; payload: number }
| { type: CartActionType.APPLY_DISCOUNT; payload: number };
// 初始化购物车状态
const initialCartState: CartState = {
items: [],
totalPrice: 0,
discount: 0
};
// reducer 函数
const cartReducer = (state: CartState, action: CartAction): CartState => {
switch (action.type) {
case CartActionType.ADD_ITEM:
return {
...state,
items: [...state.items, action.payload],
totalPrice: state.totalPrice + action.payload.price
};
case CartActionType.REMOVE_ITEM:
const removedItem = state.items.find(item => item.id === action.payload);
if (!removedItem) return state;
return {
...state,
items: state.items.filter(item => item.id!== action.payload),
totalPrice: state.totalPrice - removedItem.price * removedItem.quantity
};
case CartActionType.INCREMENT_QUANTITY:
return {
...state,
items: state.items.map(item =>
item.id === action.payload
? {...item, quantity: item.quantity + 1 }
: item
),
totalPrice: state.totalPrice + state.items.find(item => item.id === action.payload)?.price || 0
};
case CartActionType.DECREMENT_QUANTITY:
return {
...state,
items: state.items.map(item =>
item.id === action.payload && item.quantity > 1
? {...item, quantity: item.quantity - 1 }
: item
),
totalPrice: state.totalPrice - (state.items.find(item => item.id === action.payload)?.price || 0)
};
case CartActionType.APPLY_DISCOUNT:
return {
...state,
discount: action.payload,
totalPrice: state.totalPrice - action.payload
};
default:
return state;
}
};
const CartComponent = () => {
const [cartState, dispatch] = useReducer(cartReducer, initialCartState);
return (
<div>
<h1>购物车</h1>
{/* 显示购物车商品列表、总价等信息 */}
</div>
);
};
export default CartComponent;
2. 利用 action
不同类型处理状态变化
ADD_ITEM
: 当需要向购物车添加商品时,触发此action
。在reducer
中,将新商品添加到items
数组,并更新totalPrice
。REMOVE_ITEM
: 移除购物车中的某个商品。reducer
过滤掉要移除的商品,并相应减少totalPrice
。INCREMENT_QUANTITY
: 增加指定商品的数量。reducer
找到对应商品并增加其数量,同时更新totalPrice
。DECREMENT_QUANTITY
: 减少指定商品的数量(但数量需大于1)。reducer
找到对应商品并减少其数量,同时更新totalPrice
。APPLY_DISCOUNT
: 应用促销活动(如满减)。reducer
更新discount
并从totalPrice
中减去折扣金额。
3. 优化性能,避免不必要的重新渲染
- 使用
React.memo
: 对于购物车组件的子组件,如果其props
没有变化,使用React.memo
包裹可以避免不必要的重新渲染。例如,如果有一个展示单个商品信息的子组件,只要该商品的信息(如name
、price
、quantity
)没有改变,就不会重新渲染。
const ItemComponent = React.memo(({ item }: { item: CartItem }) => {
return (
<div>
<p>{item.name}</p>
<p>价格: {item.price}</p>
<p>数量: {item.quantity}</p>
</div>
);
});
- 拆分状态: 如果购物车组件的某些状态变化频率较低,可以将其拆分成独立的状态管理。例如,促销活动的配置信息可能不经常变化,可以将其放在一个单独的
useState
或useReducer
中管理,这样当购物车商品列表变化时,不会触发与促销活动配置相关的子组件的重新渲染。 - 使用
useCallback
和useMemo
: 在传递给子组件的函数或计算值可能导致子组件重新渲染时,使用useCallback
缓存函数,useMemo
缓存计算值。例如,如果有一个函数用于计算购物车中商品的总重量(假设商品有重量属性),并且这个函数传递给一个子组件,可以使用useMemo
缓存计算结果,避免每次父组件渲染时都重新计算。
const totalWeight = useMemo(() => {
return cartState.items.reduce((acc, item) => acc + item.weight * item.quantity, 0);
}, [cartState.items]);
const handleIncrement = useCallback((id: number) => {
dispatch({ type: CartActionType.INCREMENT_QUANTITY, payload: id });
}, []);