React Reducer

随着组件设计的更加复杂,状态更新就会变得困难,所以需要借助 reducer 帮助我们绑定管理一系列状态。

useState 跟 useReducer 完全可以平替,只不过建议当 state 达到 3 个以上的时候可以考虑换成 useReducer 增加代码可读性。少量的 state 用 useState 钩子完全可以应付。

如何从 useState 转到 useReducer

删处 state 逻辑,只保留操作函数例如 handleSomething(),取而代之的是分配动作

function handleSomething() {
  dispatch(
    // action object
    {
    type: '自定义的操作',
    ...
    }
  );
}

对象中通常有一个 type 属性用来区分需要干什么

写 reducer 函数 接受两个参数,第一个参数是状态 state,第二个参数是行为 action,返回值是新的状态 state。如果程序有多个状态而一个 action 只需要负责一个状态的改变时,剩余状态应保持原样直接返回。


function stateReducer(state, action) {
    ...
}

组件中使用 reducer 此时需要引入钩子函数 useReducer。它接受两个参数,第一个为步骤 2 里的 reducer 函数,第二个为状态初始值


const [state, dispatch] = useReducer(stateReducer, initialState);

代码实战



首先只用 useState 实现一个计数器

import { useState } from "react";
export default function App() {
  const [number, setNumber] = useState(0);
  const handleAdd = () => {
    setNumber((prev) => prev + 1);
  };
  const handleMinus = () => {
    setNumber((prev) => prev - 1);
  };
  return (
    <div className="App">
      <div>{number}</div>
      <button onClick={handleAdd}>Add</button>
      <button onClick={handleMinus}>Minus</button>
    </div>
  );
}
现在我们开始改写
删掉 state 设置逻辑,取而代之的是分配动作

import { useState } from "react";
export default function App() {
  const [number, setNumber] = useState(0);
  const handleAdd = () => {
    dispatch({ type: "add" });
  };
  const handleMinus = () => {
    dispatch({ type: "minus" });
  };
  return (
    <div className="App">
      <div>{number}</div>
      <button onClick={handleAdd}>Add</button>
      <button onClick={handleMinus}>Minus</button>
    </div>
  );
}
在上述代码中,动作 action 指的就是`{ type: "add" }`和`{ type: "minus" }`
写 reducer 函数,
tip. 通常使用 switch 而不是 if statement.

const numberReducer = (number, action) => {
  switch (action.type) {
    case "add": {
      return {
        number + 1;
      }
    }
    case "minus": {
      return {
        number - 1;
      }
    }
    default: {
      return number;
    }
  }
};
组件中使用 reducer 函数

import { useReducer } from "react";
export default function App() {
  const [number, dispatch] = useReducer(numberReducer, 0);
  const handleAdd = () => {
    dispatch({ type: "add" });
  };
  const handleMinus = () => {
    dispatch({ type: "minus" });
  };
  return (
    <div className="App">
      <div>{number}</div>
      <button onClick={handleAdd}>Add</button>
      <button onClick={handleMinus}>Minus</button>
    </div>
  );
}