React 中hooks之useReducer使用场景和方法总结

ops/2025/1/18 20:08:35/

1. useReducer 基本概念

useReducer 是 React 的一个 Hook,用于管理复杂的状态逻辑。它接收一个 reducer 函数和初始状态,返回当前状态和 dispatch 函数。

1.1 基本语法

const [state, dispatch] = useReducer(reducer, initialState, init);
  • reducer: (state, action) => newState
  • initialState: 初始状态
  • init: (可选) 惰性初始化函数

2. 基础示例

2.1 简单计数器

function counterReducer(state, action) {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };default:return state;}
}function Counter() {const [state, dispatch] = useReducer(counterReducer, { count: 0 });return (<div>Count: {state.count}<button onClick={() => dispatch({ type: 'increment' })}>+</button><button onClick={() => dispatch({ type: 'decrement' })}>-</button></div>);
}

3. 复杂状态管理示例

3.1 待办事项列表

const todoReducer = (state, action) => {switch (action.type) {case 'ADD_TODO':return {...state,todos: [...state.todos, {id: Date.now(),text: action.payload,completed: false}]};case 'TOGGLE_TODO':return {...state,todos: state.todos.map(todo =>todo.id === action.payload? { ...todo, completed: !todo.completed }: todo)};case 'DELETE_TODO':return {...state,todos: state.todos.filter(todo => todo.id !== action.payload)};default:return state;}
};function TodoApp() {const [state, dispatch] = useReducer(todoReducer, { todos: [] });const [input, setInput] = useState('');const handleSubmit = (e) => {e.preventDefault();if (!input.trim()) return;dispatch({ type: 'ADD_TODO', payload: input });setInput('');};return (<div><form onSubmit={handleSubmit}><inputvalue={input}onChange={e => setInput(e.target.value)}placeholder="添加待办事项"/><button type="submit">添加</button></form><ul>{state.todos.map(todo => (<li key={todo.id}><spanstyle={{textDecoration: todo.completed ? 'line-through' : 'none'}}onClick={() => dispatch({type: 'TOGGLE_TODO',payload: todo.id})}>{todo.text}</span><button onClick={() => dispatch({type: 'DELETE_TODO',payload: todo.id})}>删除</button></li>))}</ul></div>);
}

3.2 表单状态管理

const formReducer = (state, action) => {switch (action.type) {case 'SET_FIELD':return {...state,[action.field]: action.value};case 'SET_ERROR':return {...state,errors: {...state.errors,[action.field]: action.error}};case 'RESET_FORM':return initialState;default:return state;}
};function ComplexForm() {const initialState = {username: '',email: '',password: '',errors: {}};const [state, dispatch] = useReducer(formReducer, initialState);const handleChange = (e) => {const { name, value } = e.target;dispatch({type: 'SET_FIELD',field: name,value});// 验证逻辑if (name === 'email' && !value.includes('@')) {dispatch({type: 'SET_ERROR',field: 'email',error: '请输入有效的邮箱地址'});}};return (<form><div><inputname="username"value={state.username}onChange={handleChange}placeholder="用户名"/></div><div><inputname="email"value={state.email}onChange={handleChange}placeholder="邮箱"/>{state.errors.email && (<span style={{ color: 'red' }}>{state.errors.email}</span>)}</div><div><inputname="password"type="password"value={state.password}onChange={handleChange}placeholder="密码"/></div><button type="button" onClick={() => dispatch({ type: 'RESET_FORM' })}>重置</button></form>);
}

4. 使用 Immer 简化 Reducer 逻辑

Immer 允许我们以更直观的方式编写 reducer,无需手动处理不可变性。

4.1 安装 Immer

npm install immer

4.2 使用 Immer 重写 Todo 示例

import produce from 'immer';const todoReducer = produce((draft, action) => {switch (action.type) {case 'ADD_TODO':draft.todos.push({id: Date.now(),text: action.payload,completed: false});break;case 'TOGGLE_TODO':const todo = draft.todos.find(t => t.id === action.payload);if (todo) {todo.completed = !todo.completed;}break;case 'DELETE_TODO':const index = draft.todos.findIndex(t => t.id === action.payload);if (index !== -1) {draft.todos.splice(index, 1);}break;}
});

4.3 使用 Immer 简化复杂状态更新

具体参照:[https://immerjs.github.io/immer/zh-CN/example-setstate]

const complexReducer = produce((draft, action) => {switch (action.type) {case 'UPDATE_NESTED_STATE':draft.users[action.userId].preferences.theme = action.theme;break;case 'ADD_ITEM_TO_ARRAY':draft.items[action.categoryId].push(action.item);break;case 'UPDATE_MULTIPLE_FIELDS':Object.assign(draft.form, action.updates);break;}
});function ComplexStateComponent() {const [state, dispatch] = useReducer(complexReducer, {users: {},items: {},form: {}});// 使用示例const updateTheme = (userId, theme) => {dispatch({type: 'UPDATE_NESTED_STATE',userId,theme});};const addItem = (categoryId, item) => {dispatch({type: 'ADD_ITEM_TO_ARRAY',categoryId,item});};
}

5. useReducer 使用场景

  1. 复杂的状态逻辑:当组件状态逻辑复杂,包含多个值时
  2. 相关状态更新:当多个状态更新紧密相关时
  3. 状态依赖其他状态:当状态更新依赖于其他状态值时
  4. 深层状态更新:当需要更新深层嵌套的状态时
  5. 状态更新需要集中管理:当需要在一个地方管理所有状态更新逻辑时

6. 最佳实践

  1. Action 类型常量化
const TODO_ACTIONS = {ADD: 'ADD_TODO',TOGGLE: 'TOGGLE_TODO',DELETE: 'DELETE_TODO'
};
  1. Action Creator 函数化
const createTodo = (text) => ({type: TODO_ACTIONS.ADD,payload: text
});
  1. 使用 TypeScript 定义类型
interface Todo {id: number;text: string;completed: boolean;
}type TodoAction = | { type: 'ADD_TODO'; payload: string }| { type: 'TOGGLE_TODO'; payload: number }| { type: 'DELETE_TODO'; payload: number };const todoReducer = (state: Todo[], action: TodoAction): Todo[] => {// reducer 逻辑
};
  1. 合理拆分 Reducer
const rootReducer = (state, action) => {return {todos: todosReducer(state.todos, action),user: userReducer(state.user, action),ui: uiReducer(state.ui, action)};
};

通过使用 useReducer 和 Immer,我们可以更好地管理复杂的状态逻辑,同时保持代码的可读性和可维护性。Immer 特别适合处理深层嵌套的状态更新,让代码更简洁直观。


http://www.ppmy.cn/ops/151173.html

相关文章

【2024年华为OD机试】(B卷,100分)- 数据分类 (Java JS PythonC/C++)

一、问题描述 题目描述 对一个数据a进行分类&#xff0c;分类方法为&#xff1a; 此数据a&#xff08;四个字节大小&#xff09;的四个字节相加对一个给定的值b取模&#xff0c;如果得到的结果小于一个给定的值c&#xff0c;则数据a为有效类型&#xff0c;其类型为取模的值&…

android T 建立文件夹及文件的记录

第一&#xff1a;AndroidManifest.xml 中整体给予apk权限&#xff0c;如此加入后&#xff0c;在android的settings中&#xff0c;可以找到app.手动给予静态的权限&#xff0c;但是app不一定能使用&#xff0c;请大神指导为什么&#xff1f; <uses-permission android:name&q…

支付宝P0级重大事故!

支付宝又出事了&#xff01;支付宝发生了P0级的重大事故&#xff0c;导致了大规模的用户体验问题&#xff0c;引起了广泛关注。尽管支付宝已经是国内最成熟、最稳定的支付平台之一&#xff0c;但这次的事故再次让人感受到&#xff1a;无论多么强大的平台&#xff0c;也无法避免…

基于Python(Django)+SQLite3实现的(Web)资产管理系统

基于python和Django实现的资产管理系统 第一章 绪 论 1.1 研究工作的背景与意义 当今社会正快速向信息化社会前进。信息系统的作用也越来越大&#xff0c;特别是一些企业或组织等&#xff0c;往往需要管理大量的资产数据。对这些资产信息&#xff0c;往往需要安全的&#xf…

25. 【.NET 8 实战--孢子记账--从单体到微服务】--记账模块--预算金额的币种转换

这篇文章的代码和22. 【.NET 8 实战–孢子记账–从单体到微服务】–记账模块–切换主币种这篇文章的代码大体一样。这篇文章我们简单讲解一下。 既然收支记录都已经支持主币种转换的同时重新计算收支金额了&#xff0c;那预算也要支持切换主币种重新计算预算金额。我们一起来看…

nssctf_WEB部分题解

1.题目:easy_ssrf(ssrf 伪协议读取文件) 1. 题目:![](https://cdn.nlark.com/yuque/0/2024/png/39210681/1734314732935-4ccb798a-8525-4439-90e5-b78551cbfea0.png)看起来没有什么思路,于是随便写个网站看看回显:![](https://cdn.nlark.com/yuque/0/2024/png/39210681/1734314…

Flask学习入门笔记

Flask学习入门笔记 前言1. 安装Flask2. 创建一个简单的Flask应用3. 路由与视图函数3.1 基本路由3.2 动态路由3.3 HTTP方法 4. 请求与响应4.1 获取请求数据4.2 返回响应 5. 模板渲染5.1 基本模板渲染5.2 模板继承 6. 静态文件6.1 静态文件的目录结构6.2 在模板中引用静态文件6.2…

Temp123

MapDB&#xff1a;的持久化机制&#xff0c;以及源码分析和摘取 1、spark streaming--struct streaming 基于 时间间隔 攒批 2、kafka-connect-hdfs 控制 flush.size 和 interval.ms控制 攒批 - 完全自研 攒批机制 - 使用 embeded 版 https://lxblog.com/qianwen/share?shar…