发布于 2026-01-06 1 阅读
0

尝试使用 reducer 进行状态管理。那么什么是 reducer 呢?useReducer hook!DEV 全球项目展示挑战赛,由 Mux 主办:展示你的项目!

尝试使用 reducer 进行状态管理。

那么,什么是 Reducer?

useReducer 钩子!

由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!

你以前听说过减速器吗?

当然啦,哥们儿,你在说什么呢?我简直爱死减法器了。我到处都用减法器,就连最简单的计数器应用里也用。

太好了!这篇文章不适合你😊。

如果你听说过 reducer,但从听说 reducer 的那一刻起就一直拖延,因为你觉得没有必要去学习这个过于复杂、错综复杂、曲折难懂的东西,而是想在所有需要管理状态的地方都使用 useState,那么你很幸运🍀。

在我看来,Reducer 是一种非常有趣的状态管理方式,它具有多种优点,我们将在本文中看到这一点。

那么,什么是 Reducer?

其实它只是个函数,没什么特别的。它接收参数,然后返回一个值,就像所有普通函数一样。

我们来看一下参数和返回值,好吗?

因此,每个 reducer 函数都会接收应用程序的初始状态和action(就像说“这是发生的事情”),并在 action 发生后抛出应用程序的新状态。

现在,在创建了这个 reducer 函数之后,你只需要分发 action 来更新状态即可。这就是 reducer 模式的基本原理。

开始建造!

好的!我们来创建一个div元素,并使用reducer来修改它的CSS属性。我喜欢先单独编写reducer,思考我想要执行哪些操作。

const INCREASE_WIDTH = "INCREASE_WIDTH";
const TOGGLE_FILL = "TOGGLE_FILL";

const cssReducer = (state, action) => {
  if (action.type === INCREASE_WIDTH) {
    return { ...state, width: state.width + 5 };
  }

  if (action.type === TOGGLE_FILL) {
    return {
      ...state,
      backgroundColor: state.backgroundColor === "white" ? "plum" : "white"
    };
  }

  return state;
};
Enter fullscreen mode Exit fullscreen mode

所以,我想增加这个框的宽度,并切换背景颜色。如您所见,我在顶部定义了两个操作类型:INCREASE_WIDTHTOGGLE_FILL,它们都是字符串类型,这样做是为了防止拼写错误。如果您弄错了常量名称,就会弹出错误消息;而如果您到处都使用字符串,即使拼错了一个,也不会弹出任何消息。此外,它们还有助于自动补全功能,所以不妨试试吧👍🏻。

reducer 函数接收初始状态,并根据操作类型相应地改变状态并返回,从而更新视图。

你明白它的好处了吗?因为它只是一个函数,所以测试起来非常容易,只需提供初始状态对象和动作对象,它就会返回一个可预测的最终状态对象。完美🧈。

PS:如果你现在有点困惑也没关系,因为你刚刚看到了 reducer 函数,当我们在应用程序中完全实现它时,这一切就会变得有意义了,接下来就是实现它了。

useReducer 钩子!

useReducer 是 React 预构建的 hooks 之一,它使我们能够非常轻松地实现 reducer 模式,而无需借助 Redux 等外部库。

它缺少 Redux 的一些开箱即用的功能(例如应用中间件),所以这取决于你的应用程序是否需要这些功能。大多数情况下你不需要,但这最终还是需要你自己做出选择。

以下是使用方法

const [styles, dispatch] = useReducer(cssReducer, initialStyles);
Enter fullscreen mode Exit fullscreen mode

useReducer 接受 reducer 和初始状态作为参数,并返回一个包含两部分的数组,第一部分是应用程序在任何给定时间的状态,第二部分是用于分发 action 的 dispatch 函数。

让我们一起编写所有代码,这样你就可以看到调度函数的实际运行情况了。

import React, { useReducer } from "react";

const INCREASE_WIDTH = "INCREASE_WIDTH";
const TOGGLE_FILL = "TOGGLE_FILL";

const initialStyles = {
  border: "3px solid plum",
  height: 100,
  width: 100,
  backgroundColor: "white"
};

const cssReducer = (state, action) => {
  if (action.type === INCREASE_WIDTH) {
    return { ...state, width: state.width + action.payload.step };
  }

  if (action.type === TOGGLE_FILL) {
    return {
      ...state,
      backgroundColor: state.backgroundColor === "white" ? "plum" : "white"
    };
  }

  return state;
};

export default function App() {
  const [styles, dispatch] = useReducer(cssReducer, initialStyles);

  return (
    <div className="App">
      <div style={styles}></div>
      <button
        onClick={() => {
          dispatch({
            type: INCREASE_WIDTH,
            payload: {
              step: 10,
            }
          });
        }}
      >
        Increase Width
      </button>
      <button
        onClick={() => {
          dispatch({
            type: TOGGLE_FILL
          });
        }}
      >
        Toggle Fill
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

这里我们通过按钮点击来分发 action。action 对象至少应该包含一个 type 属性,但我们也可以传递更多关于 action 的信息,通常使用名为payload的键,就像我们在这里传递 step 属性一样,并且我们稍微修改了 reducer 函数来使用这个键。

这里有一个可供体验的在线演示。

另一个显而易见的优势是,它将所有逻辑从组件本身剥离出来,组件现在只负责渲染 HTML。您可以将 reducer 逻辑放在另一个文件中(甚至可以创建一个专门用于所有 action 的文件),从而使代码更易于维护。

当有很多活动部件时,Reducer 非常有用,例如在有很多字段的表单中,与其为每个字段使用 useState,不如尝试使用 reducer。

您还可以使用 reducer 从外部源获取数据并处理请求的所有不同阶段。

以下是我的看法。

import React, { useEffect, useReducer } from "react";

const REQUEST_LOADING = "REQUEST_LOADING";
const FETCH_SUCCESS = "FETCH_SUCCESS";
const FETCH_FAILURE = "FETCH_FAILURE";

const initialState = {
  loading: false,
  data: null,
  error: null
};

const fetchReducer = (state, action) => {
  if (action.type === REQUEST_LOADING) {
    return {
      date: null,
      loading: true,
      error: null
    };
  }

  if (action.type === FETCH_SUCCESS) {
    return {
      data: action.payload.response,
      loading: false,
      error: null
    };
  }

  if (action.type === FETCH_FAILURE) {
    return {
      data: null,
      error: action.payload.error,
      loading: false
    };
  }

  return state;
};

export default function App() {
  const [{ loading, data, error }, dispatch] = useReducer(
    fetchReducer,
    initialState
  );

  useEffect(() => {
    dispatch({ type: REQUEST_LOADING });

    fetch("some url")
      .then((res) => res.json())
      .then((response) => {
        console.log(response);
        dispatch({ type: FETCH_SUCCESS, payload: { response } });
      })
      .catch((err) => {
        dispatch({ type: FETCH_FAILURE, payload: { error: err } });
      });
  }, []);

  if (error) return <p>{error.message}</p>;

  return (
    <div className="App">
      {loading ? <p>Loading...</p> : <p>{data.setup}</p>}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

如果使用得当,reducers 的确有一些好处,但不要像开头那个人一样到处乱用。对于简单的状态管理,useState 就完全够用了。

文章来源:https://dev.to/tusharkashyap63/everything-about-reducers-395f