一、简单场景:用 useState
1. 只有一个简单的计数器
适合: 状态简单,就一两个值
scss
const [count, setCount] = useState(0)
setCount(count + 1) // 简单!
二、 复杂场景:必须用 useReducer
场景1:状态逻辑复杂
用 useState(乱七八糟)
scss
const [items, setItems] = useState([])
const [total, setTotal] = useState(0)
const [discount, setDiscount] = useState(0)
const [tax, setTax] = useState(0)
// 添加商品 - 要改4个地方!
const addItem = (item) => {
setItems([...items, item])
setTotal(total + item.price)
setTax((total + item.price) * 0.1)
setDiscount(calculateDiscount(total + item.price))
// 容易漏改,容易出 bug!
}
// 删除商品 - 又要改4个地方!
const removeItem = (id) => {
const item = items.find(i => i.id === id)
setItems(items.filter(i => i.id !== id))
setTotal(total - item.price)
setTax((total - item.price) * 0.1)
setDiscount(calculateDiscount(total - item.price))
// 太容易出错了!
}
用 useReducer(清晰)
php
const [state, dispatch] = useReducer(reducer, {
items: [],
total: 0,
discount: 0,
tax: 0
})
// 添加商品 - 只需要一句话!
const addItem = (item) => {
dispatch({ type: 'ADD_ITEM', payload: item })
// 所有逻辑都在 reducer 里,不会漏
}
// 删除商品 - 也是一句话!
const removeItem = (id) => {
dispatch({ type: 'REMOVE_ITEM', payload: id })
// 简单清晰
}
// reducer 里统一处理所有逻辑
function reducer(state, action) {
switch(action.type) {
case 'ADD_ITEM':
const newTotal = state.total + action.payload.price
return {
items: [...state.items, action.payload],
total: newTotal,
tax: newTotal * 0.1,
discount: calculateDiscount(newTotal)
}
case 'REMOVE_ITEM':
// 所有相关逻辑都在这里,集中管理
}
}
场景2:多个操作修改同一个状态
用 useState(代码重复)
scss
// 修改名字
const updateName = (name) => {
setUser({ ...user, name })
}
// 修改年龄
const updateAge = (age) => {
setUser({ ...user, age })
}
// 修改城市
const updateCity = (city) => {
setUser({ ...user, city })
}
// 重置
const reset = () => {
setUser({ name: '', age: 0, city: '' })
}
// 每个函数都在写 setUser({...user, ...}),好烦!
用 useReducer(统一管理)
php
const [user, dispatch] = useReducer(userReducer, { name: '', age: 0, city: '' })
// 一个函数搞定所有操作
const update = (field, value) => {
dispatch({ type: 'UPDATE', field, value })
}
// 或者更清晰的方式
dispatch({ type: 'UPDATE_NAME', payload: '张三' })
dispatch({ type: 'UPDATE_AGE', payload: 25 })
dispatch({ type: 'RESET' })
// reducer 统一处理
function userReducer(state, action) {
switch(action.type) {
case 'UPDATE_NAME':
return { ...state, name: action.payload }
case 'UPDATE_AGE':
return { ...state, age: action.payload }
case 'UPDATE_CITY':
return { ...state, city: action.payload }
case 'RESET':
return { name: '', age: 0, city: '' }
}
}
场景3:下一个状态依赖当前状态
用 useState(可能出错)
scss
const [count, setCount] = useState(0)
// 快速点3次按钮
onClick={() => {
setCount(count + 1) // count = 0,变成 1
setCount(count + 1) // count 还是 0,又变成 1
setCount(count + 1) // count 还是 0,还是变成 1
// 最后 count = 1,而不是 3!
}}
// 需要用函数式更新
setCount(prev => prev + 1) // 这样才对
用 useReducer(不会出错)
php
const [state, dispatch] = useReducer(reducer, { count: 0 })
onClick={() => {
dispatch({ type: 'INCREMENT' })
dispatch({ type: 'INCREMENT' })
dispatch({ type: 'INCREMENT' })
// 不会出错,肯定是 +3
}}
function reducer(state, action) {
switch(action.type) {
case 'INCREMENT':
return { count: state.count + 1 } // 总是用最新的 state
}
}
三、 什么时候用 useReducer or useState?
1. 用 useState ✅
scss
// 1. 简单值
const [name, setName] = useState('')
const [age, setAge] = useState(0)
const [isOpen, setIsOpen] = useState(false)
// 2. 独立的状态
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
// 3. 不相关的多个值
const [count1, setCount1] = useState(0)
const [count2, setCount2] = useState(0)
2. 用 useReducer ✅
php
// 1. 购物车(多个相关值)
const [cart, dispatch] = useReducer(cartReducer, {
items: [],
total: 0,
discount: 0
})
// 2. 表单(多个字段)
const [form, dispatch] = useReducer(formReducer, {
name: '',
email: '',
phone: '',
address: ''
})
// 3. 复杂的状态机
const [state, dispatch] = useReducer(reducer, {
status: 'idle', // idle -> loading -> success -> error
data: null,
error: null
})
四、形象比喻
1. useState = 自己管钱
scss
// 你有3个钱包
const [工资, set工资] = useState(5000)
const [奖金, set奖金] = useState(1000)
const [存款, set存款] = useState(10000)
// 发工资了
set工资(6000)
set存款(存款 + 1000) // 还要记得更新存款
2.useReducer = 请财务帮你管钱
scss
const [账户, dispatch] = useReducer(财务管理, {
工资: 5000,
奖金: 1000,
存款: 10000
})
// 发工资了,告诉财务一声
dispatch({ type: '发工资', 金额: 1000 })
// 财务会自动帮你:
// 1. 工资加1000
// 2. 存款也加1000
// 3. 更新账户余额
// 不会漏掉任何步骤!
五、总结
为什么用 useReducer 而不用 useState?
