React hook之userReducer

在 React 中,useReducer 是一个用于管理复杂状态逻辑的 Hook,它类似于 Redux 中的 reducer 模式,但更轻量且适用于组件内部或结合 Context API 实现全局状态管理。以下是 useReducer 的详细用法指南:


1. 基本语法

javascript 复制代码
const [state, dispatch] = useReducer(reducer, initialState);
  • reducer:一个函数,接收当前状态和 action,返回新状态。
  • initialState:状态的初始值。
  • state:当前状态。
  • dispatch:用于触发状态更新的函数(发送 action)。

2. 核心概念

(1) Reducer 函数

Reducer 的格式:(state, action) => newState

  • action :通常是一个对象,包含 type(操作类型)和可选的 payload(数据)。
  • 必须返回新状态(不可直接修改原状态!)。
javascript 复制代码
function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'DECREMENT':
      return { ...state, count: state.count - 1 };
    case 'SET_VALUE':
      return { ...state, value: action.payload };
    default:
      return state; // 默认返回原状态
  }
}
(2) Dispatch Action

通过 dispatch 发送 action 来更新状态:

javascript 复制代码
dispatch({ type: 'INCREMENT' });
dispatch({ type: 'SET_VALUE', payload: 42 });

3. 完整示例

计数器组件
jsx 复制代码
import { useReducer } from 'react';

// 定义 reducer
function counterReducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    case 'RESET':
      return { count: 0 };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
      <button onClick={() => dispatch({ type: 'RESET' })}>Reset</button>
    </div>
  );
}

4. 进阶用法

(1) 结合 Payload 传递数据
javascript 复制代码
dispatch({ type: 'SET_NAME', payload: 'Alice' });

// Reducer 处理
case 'SET_NAME':
  return { ...state, name: action.payload };
(2) 惰性初始化

如果初始状态需要计算,可以传入一个初始化函数:

javascript 复制代码
function init(initialCount) {
  return { count: initialCount };
}

const [state, dispatch] = useReducer(reducer, initialCount, init);
(3) 结合 Context API 实现全局状态
jsx 复制代码
// 创建 Context
const CounterContext = createContext();

// Provider 组件
function CounterProvider({ children }) {
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });
  return (
    <CounterContext.Provider value={{ state, dispatch }}>
      {children}
    </CounterContext.Provider>
  );
}

// 子组件中使用
function ChildComponent() {
  const { state, dispatch } = useContext(CounterContext);
  // ...
}

5. 与 useState 的对比

场景 useState useReducer
简单状态 适合(如布尔值、数字) 过度设计
复杂状态逻辑 代码冗长(需多个 useState 适合(逻辑集中管理)
依赖前一个状态 需用函数更新(setCount(c => c + 1) 自动处理(reducer 接收当前状态)
跨组件共享状态 需结合 Context 更适合(与 Context 天然搭配)

6. 最佳实践

  1. 拆分 Reducer

    如果逻辑复杂,可以按功能拆分成多个 reducer,再用 combineReducers(类似 Redux):

    javascript 复制代码
    function rootReducer(state, action) {
      return {
        counter: counterReducer(state.counter, action),
        user: userReducer(state.user, action),
      };
    }
  2. 避免深层嵌套状态

    尽量扁平化状态结构,便于更新。

  3. 异步操作

    dispatch 外处理异步逻辑(如 API 请求),完成后调用 dispatch

    javascript 复制代码
    async function fetchData(dispatch) {
      const data = await api.get();
      dispatch({ type: 'SET_DATA', payload: data });
    }

7. 示例:Todo 列表

jsx 复制代码
function todoReducer(state, action) {
  switch (action.type) {
    case 'ADD_TODO':
      return [...state, { text: action.payload, completed: false }];
    case 'TOGGLE_TODO':
      return state.map((todo, index) =>
        index === action.payload
          ? { ...todo, completed: !todo.completed }
          : todo
      );
    default:
      return state;
  }
}

function TodoApp() {
  const [todos, dispatch] = useReducer(todoReducer, []);
  const [input, setInput] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    dispatch({ type: 'ADD_TODO', payload: input });
    setInput('');
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={(e) => setInput(e.target.value)} />
      </form>
      <ul>
        {todos.map((todo, index) => (
          <li
            key={index}
            onClick={() => dispatch({ type: 'TOGGLE_TODO', payload: index })}
            style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
          >
            {todo.text}
          </li>
        ))}
      </ul>
    </div>
  );
}

总结

  • 何时用 useReducer
    状态逻辑复杂、需要依赖前一个状态、或需要跨组件共享状态时。
  • 优势:逻辑集中、易于测试、适合与 Context 结合。
  • 注意 :避免过度使用,简单场景直接用 useState 更高效。
相关推荐
举个栗子dhy2 小时前
解决在父元素上同时使用 onMouseEnter和 onMouseLeave时导致下拉菜单无法正常展开或者提前收起问题
前端·javascript·react.js
薛定谔的算法3 小时前
《虚拟 DOM 与 Diff 算法:用 1500 字把它讲成“人话”》
前端·react.js·前端框架
鹏多多3 小时前
前端项目eslint配置选项详细解析
前端·vue.js·react.js
知识分享小能手4 小时前
React学习教程,从入门到精通,React 使用属性(Props)创建组件语法知识点与案例详解(15)
前端·javascript·vue.js·学习·react.js·前端框架·vue
牧羊狼的狼9 小时前
React 中的 HOC 和 Hooks
前端·javascript·react.js·hooks·高阶组件·hoc
知识分享小能手10 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
魔云连洲10 小时前
深入解析:Vue与React的异步批处理更新机制
前端·vue.js·react.js