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

1. useReducer 基本概念

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

1.1 基本语法

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

2. 基础示例

2.1 简单计数器

jsx 复制代码
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 待办事项列表

jsx 复制代码
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 表单状态管理

jsx 复制代码
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

bash 复制代码
npm install immer

4.2 使用 Immer 重写 Todo 示例

jsx 复制代码
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\]

jsx 复制代码
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 类型常量化
jsx 复制代码
const TODO_ACTIONS = {
  ADD: 'ADD_TODO',
  TOGGLE: 'TOGGLE_TODO',
  DELETE: 'DELETE_TODO'
};
  1. Action Creator 函数化
jsx 复制代码
const createTodo = (text) => ({
  type: TODO_ACTIONS.ADD,
  payload: text
});
  1. 使用 TypeScript 定义类型
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
jsx 复制代码
const rootReducer = (state, action) => {
  return {
    todos: todosReducer(state.todos, action),
    user: userReducer(state.user, action),
    ui: uiReducer(state.ui, action)
  };
};

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

相关推荐
LikM6 分钟前
tldr 快速查阅命令
前端·后端
无限大.28 分钟前
基于 HTML5 Canvas 制作一个精美的 2048 小游戏--day 1
前端·html·html5
爱编程的鱼34 分钟前
HTML5 教程(下)
前端·html·html5
web150854159351 小时前
Node.js npm 安装过程中 EBUSY 错误的分析与解决方案
前端·npm·node.js
涔溪1 小时前
使用 electron-builder 构建一个 Electron 应用程序
前端·javascript·electron
shmily麻瓜小菜鸡1 小时前
el-table中使用el-image图片预览被其他表格遮挡,使用z-index层级设置无效
前端·javascript·vue.js
qq_530245191 小时前
自定义提示确认弹窗-vue
前端·vue.js·typescript
风茫1 小时前
掌握 TypeScript 的 `Omit` 工具类型:灵活操作对象属性的艺术
javascript·ubuntu·typescript
远洋录1 小时前
Electron 开发者的 Tauri 2.0 实战指南:安全实践
前端·人工智能·react
m0_748231312 小时前
【前端】Node.js使用教程
前端·node.js·vim