react hook: useReducer

时间:2024-03-18 20:48:26

useReducer 相当于 Vuex 中的 store 和 mutations 的结合。
useReducer 更适用于处理复杂的状态逻辑,尤其是当状态之间存在关联,或者需要多个状态同时更新时。可以让我们的代码具有更好的可读性、可维护性、可预测性。

const [state, dispatch] = useReducer(reducer, initialArg, init?)
在组件的顶层作用域调用 useReducer 以创建一个用于管理状态的 reducer。
reducer:用于更新 state 的纯函数。参数为 state 和 action,返回值是更新后的 state。state 与 action 可以是任意合法值。(往函数体里面添加计算并返回新的 state 的逻辑)

initialArg:用于初始化 state 的任意值。初始值的计算逻辑取决于接下来的 init 参数。
dispatch 函数。用于更新 state 并触发组件的重新渲染。
严格模式下 React 会 调用两次 reducer 和初始化函数
dispatch(action)
action: 用户执行的操作。可以是任意类型的值。通常来说 action 是一个对象,其中 type 属性标识类型,其它属性携带额外信息。

import { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      return {
        name: state.name,
        age: state.age + 1
      };
    }
    case 'changed_name': {
      return {
        name: action.nextName,
        age: state.age
      };
    }
  }
  throw Error('Unknown action: ' + action.type);
}

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });
  // ...


const [state, dispatch] = useReducer(reducer, { age: 42 });

function handleClick() {
  dispatch({ type: 'incremented_age' });
  // ...

state 的类型也是任意的,不过一般会使用对象或数组。

state 是只读的。即使是对象或数组也不要尝试修改它:

//错误
function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      // ???? 不要像下面这样修改一个对象类型的 state:
      state.age = state.age + 1;
      return state;
    }
// 正确
function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      // ✅ 正确的做法是返回新的对象
      return {
        ...state,
        age: state.age + 1
      };
    }

当需要处理复杂的状态逻辑,或者需要进行多个相关状态的更新,并且这些状态需要一起进行更改时,useReducer是一个适合的选择。

表单处理
购物车功能
应用主题
数据筛选和排序
步骤导航

数组 不能使用修改方法 来更新。

function tasksReducer(tasks, action) {
  switch (action.type) {
    case 'added': {
      return [...tasks, {
        id: action.id,
        text: action.text,
        done: false
      }];
    }
    case 'deleted': {
      return tasks.filter(t => t.id !== action.id);
    }
    default: {
      throw Error('Unknown action: ' + action.type);
    }
  }
}

使用 Immer 编写简洁的更新逻辑

避免重新创建初始值

function createInitialState(username) {
  // ...
}

function TodoList({ username }) {
  const [state, dispatch] = useReducer(reducer, createInitialState(username));

虽然 createInitialState(username) 的返回值只用于初次渲染,但是在每一次渲染的时候都会被调用。如果它创建了比较大的数组或者执行了昂贵的计算就会浪费性能。

你可以通过给 useReducer 的第三个参数传入 初始化函数 来解决这个问题:

function createInitialState(username) {
  // ...
}

function TodoList({ username }) {
  const [state, dispatch] = useReducer(reducer, username, createInitialState);
  // ...

疑难看下面的示例

import { useCallback, useEffect, useRef, useReducer } from 'react';
const reducer = (state, action) => {
  const { type } = action;
  if (type === 'add') {
    return {
      ...state,
      value: state.value + 1,
    };
  } else {
    return {
      ...state,
      value: state.value - 1,
    };
  }
};
export default function useTableListHooks() {
  const [store2, dispatch] = useReducer(reducer, { value: 0, name: 'zs' });

  const clickFn = () => {
    dispatch({ type: 'aaa' });
    console.log(store2);
  };
 return {
    store2,
    clickFn,
 }
}

  const {
    store2,
    clickFn,
  } = useHooks();
<div onClick={clickFn}>
        {store2.name} - {store2.value}
      </div>