当前位置: 首页 > article >正文

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

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}>
        <input
          value={input}
          onChange={e => setInput(e.target.value)}
          placeholder="添加待办事项"
        />
        <button type="submit">添加</button>
      </form>
      <ul>
        {state.todos.map(todo => (
          <li key={todo.id}>
            <span
              style={{
                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>
        <input
          name="username"
          value={state.username}
          onChange={handleChange}
          placeholder="用户名"
        />
      </div>
      <div>
        <input
          name="email"
          value={state.email}
          onChange={handleChange}
          placeholder="邮箱"
        />
        {state.errors.email && (
          <span style={{ color: 'red' }}>{state.errors.email}</span>
        )}
      </div>
      <div>
        <input
          name="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.kler.cn/a/507577.html

相关文章:

  • MyBatis(四)参数与配置详解
  • 网络科技有限公司网络设计
  • iOS面试模版
  • SpringMVC (1)
  • ​​​​​​​​​​​​​​★3.3 事件处理
  • 我这不需要保留本地修改, 只需要拉取远程更改
  • python学opencv|读取图像(三十六)(反)零值处理
  • springboot 利用html模版导出word
  • JavaScript笔记基础篇03——函数
  • HarmonyOS使用Grid网格实现计算器功能实现
  • AndroidStudio升级到2024.2.2项目AGP升级8.8.0版本记录
  • MyBatis(三)代理Dao方式的CRUD操作
  • uniapp 微信小程序 金额展示套餐
  • 【狂热算法篇】探秘图论之 Floyd 算法:解锁最短路径的神秘密码(通俗易懂版)
  • 算法(蓝桥杯)贪心算法5——删数问题的解题思路
  • Titans Learning to Memorize at Test Time
  • AI编程工具使用技巧——通义灵码
  • 《火焰烟雾检测开源神经网络模型:智能防火的科技护盾》
  • Python调用go语言编译的库
  • Math Reference Notes: 矩阵基础
  • Android adb 调试,不在手机上点击信任 “允许usb调试” 即可连接的方式(手机需root)
  • 浅谈云计算20 | OpenStack管理模块(下)
  • CV与NLP经典大模型解读
  • RAG 切块Chunk技术总结与自定义分块实现思路
  • Node.js path.join
  • UE控件学习