外观
useReducer
作者:guo-zi-xin
更新于:2 个月前
字数统计:1.3k 字
阅读时长:5 分钟
useReducer
是React提供的一个高级Hook, 没有它我们也可以正常开发,但是useReducer
可以使我们的代码具有更好的可读性和可维护性。
useReducer
跟 useState
一样,都是帮我们去管理组件的状态的,但是与useState
不同的是,useReducer
是集中式管理状态的。
用法
typescript
const [state, dispatch] = useReducer(reducer,initialArg,init?)
参数
reducer
reducer
是一个处理函数,用于更新状态,reducer里面包含了两个参数,第一个参数是state
, 第二个参数是action
。reducer
会返回一个新的state
initialArg
initialArg
是 state的初始值
init
init
是一个可选的参数,用于初始化state
,如果编写了init函数,则默认值使用init函数的返回值,否则使用initialArg
返回值
useReducer返回一个由两个值组成的数组:
当前的state
初次渲染时, 他是init(initialArg) 或者initialArg(如果没有init函数)
dispatch函数
用于更新state并触发组件的重新渲染
typescript
import { useReducer } from 'react';
// 根据旧的状态处理 oldState, 处理完成之后返回新的状态 newState
// reducer 只有被dispatch的时候才会被调用,刚进入页面的时候是不会执行的
// oldState 仍然是只读的
const reducer = (oldState, action) => {
// ...
return newState
}
const MyComponent = () => {
const [state, dispatch] = useReducer(reducer, {age: 42, name: 'xxx'});
// ...
}
计数器案例
- 初始状态(initState):
typescript
const initialState = { count: 0 }
这里定义了一个初始状态对象,包含一个count属性,初始值为0.
- reducer函数
typescript
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1};
default:
throw new Error()
}
}
- reducer 是一个根据不同的 action 来更新状态的纯函数。
- 它接收当前状态(state)和一个动作对象(action),根据action.type来决定如何更新state。
- 如果action.type是'increment',则 count 增加1; 如果是'decrement', 则count 减少1.
- 如果action.type不匹配任何已定义的情况,则抛出一个错误。
tsx
const App = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
count: {state.count}
<button onClick={() => {dispatch({ type: 'decrement' })}}>-</button>
<button onClick={() => { dispatch({ type: 'increment' })}}>+</button>
</>
)
}
export default App
- 当点击"+"按钮时, 调用dispatch({ type: 'increment'}), 使 count 增加
- 当点击"-"按钮时, 调用dispatch({ type: 'decrement'}), 使 count 减少
购物车案例
初始数据(initData)
typescript
const initData = [
{ name: '张三', price: 100, count: 1, id: 1, isEdit: false },
{ name: '李四', price: 200, count: 1, id: 2, isEdit: false },
{ name: '王五', price: 300, count: 1, id: 3, isEdit: false },
]
initData是一个数组,表示初始的商品列表。每个商品具有以下属性:
- name:商品名称
- price: 单价
- count: 数量
- id: 商品唯一标识符
- isEdit:表示商品是否处于编辑状态,默认为false
类型定义
typescript
type List = typeof initData
interface Action {
type: 'ADD' | 'SUB' | 'DELETE' | 'EDIT' | 'UPDATE_NAME';
id: number;
newName?: string
}
List 是商品数组的类型, 直接从initData推断
Action 接口定义了不同的操作类型
- ADD: 增加某个商品的数量
- SUB: 减少某个商品的数量
- DELETE: 删除某个商品
- EDIT: 切换某个商品的编辑状态
- UPDATE_NAME: 更新某个商品的名称
- id: 需要操作的商品ID
- newName: 用于UPDATE_NAME操作时, 新的商品名称
Reducer函数
tsx
const reducer = (state:List, action: Action) => {
// 找到需要操作的商品
const item = state.find(item => item.id === action.id)!
switch (action.type) {
case 'ADD':
item.count++;
return [...state]
case 'SUB':
item.count--
return [...state]
case 'DELETE':
return state.filter(item => item.id !== action.id)
case 'EDIT':
item.isEdit = !item.isEdit;
return [...state]
case 'UPDATE_NAME':
item.name = action.newName!
return [...state]
default:
return state
}
}
reducer函数根据传入的action更新商品列表的状态。查找要操作的商品item
对不同的action.type执行相应的操作:
- ADD 将商品数量增加1
- SUB 将商品数量减少1
- DELETE 删除指定商品
- EDIT 切换商品的编辑状态
- UPDATE_NAME 更新商品的名称
App组件
tsx
const App = () => {
let [data, dispatch] = useReducer(reducer, initData)
return (<>
<table cellPadding={0} cellSpacing={0} width={600} border={1}>
<thead>
<tr>
<th>物品</th>
<th>价格</th>
<th>数量</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{data.map((item) => {
return (
<tr key={item.id}>
<td align="center">
{item.isEdit ?
<input
onBlur={e => dispatch({ type: 'EDIT', id: item.id })}
onChange={e => dispatch({ type: 'UPDATE_NAME', id: item.id, newName: e.target.value })}
value={item.name}
/>
:
<span>{item.name}</span>
}
</td>
<td align="center">{item.price * item.count}</td>
<td align="center">
<button onClick={() => dispatch({ type: 'SUB', id: item.id })}>-</button>
<button onClick={() => dispatch({ type: 'ADD', id: item.id })}>+</button>
</td>
<td align="center">
<button onClick={() => dispatch({ type: 'EDIT', id: item.id })}>编辑</button>
<button onClick={() => dispatch({ type: 'DELETE', id: item.id })}>删除</button>
</td>
</tr>
)
})}
</tbody>
<tfoot>
<tr>
<td colSpan={3}></td>
<td align="center">总价:{data.reduce((prev, next) => prev + next.price * next.count, 0)}</td>
</tr>
</tfoot>
</table>
</>)
}
App 组件使用useReducer来管理 data 状态,它从 initData 初始化, 并通过 dispatch 分发动作来改变商品列表
商品列表通过 table 渲染, 每个商品显示以下信息
- 物品: 如果该商品的isEdit为true, 显示一个输入框用于需改名称; 否则显示商品名称
- 价格: 显示商品的总价(price * count)
- 数量: 显示商品的数量,提供 - 和 + 按钮来减少或增加数量
- 操作: 提供 编辑 按钮切换名称的编辑状态、 删除 按钮可以删除该商品
- tfoot部分显示购物车的总价, 通过reduce方法计算所有商品的总价
参考文档
https://message163.github.io/react-docs/hooks/useReducer.html