为什么我停止使用 Redux 单页应用程序的问题 Redux 不是缓存 更简单的后端状态方法

2025-05-24

我为什么停止使用 Redux

单页应用程序的问题

Redux 不是缓存

一种更简单的后端状态方法

Redux是 React 生态系统中的一项革命性技术。它使我们能够拥有一个包含不可变数据的全局存储,并解决了组件树中prop-drilling的问题。对于跨应用程序共享不可变数据而言,它仍然是一款出色的工具,并且具有极佳的扩展性。

但是我们为什么需要一个全局 store 呢?我们的前端应用真的那么复杂吗?还是我们想用 Redux 做太多事情了?

单页应用程序的问题

React 等单页应用程序 (SPA) 的出现,给我们开发 Web 应用的方式带来了诸多改变。将后端代码与前端代码分离,使我们能够专注于特定领域并分离关注点。然而,它也带来了诸多复杂性,尤其是状态相关的复杂性。

现在,异步获取数据意味着数据必须存储在两个地方:前端和后端。我们必须思考如何以最佳方式全局存储这些数据,以便所有组件都能访问,同时维护数据的缓存以减少网络延迟。现在,前端开发的很大一部分负担是如何维护全局存储,避免出现状态错误、数据非规范化和数据过时等问题。

Redux 不是缓存

我们大多数人在使用 Redux 和类似的状态管理库时遇到的主要问题是,我们将其视为后端状态的缓存。我们获取数据,使用 Reducer/Action 将其添加到 Store,然后定期重新获取以确保数据是最新的。我们让 Redux 承担了太多工作,并将其作为解决所有问题的万能方案。

需要记住的一件重要事情是,我们的前端和后端状态永远不会真正同步,充其量只能营造出一种它们同步的幻觉。这是客户端-服务器模型的缺点之一,也是我们首先需要缓存的原因。然而,缓存和维护同步状态极其复杂,因此我们不应该像Redux 鼓励的那样,从头开始重新创建后端状态。

当我们开始在前端重建数据库时,后端和前端职责之间的界限很快就变得模糊。作为前端开发者,我们不需要为了创建一个简单的 UI 而对表及其关系了如指掌。我们也不需要知道如何最好地规范化我们的数据。这项责任应该落在设计表的人——后端开发者身上。这样,后端开发者就可以以文档化的 API 形式为前端开发者提供一个抽象。

现在有无数的库(例如redux-observableredux-sagaredux-thunk)围绕 Redux 构建,帮助我们管理后端数据,每个库都会给已经繁琐的库增加一层复杂性。我认为其中大多数都没有达到预期效果。有时,我们需要先退一步,再迈进一步。

如果我们不再尝试在前端代码中管理后端状态,而是将其视为只需定期更新的缓存,会怎么样?通过将前端视为从缓存中读取数据的简单显示层,我们的代码将变得非常易于使用,并且更易于纯前端开发人员访问。我们既可以获得关注点分离的所有好处,又避免了构建 SPA 的大部分弊端。

一种更简单的后端状态方法

我相信有几个库比使用 Redux(或类似的状态管理库)存储后端状态有了很大的改进。

反应查询

几个月来,我一直在大部分个人和工作项目中使用React Query
。它是一个库,API 非常简单,只包含几个钩子来管理查询(获取数据)和变更(更改数据)。 自从使用 React Query 以来,我不仅效率更高,而且比使用 Redux 时,样板代码的编写量减少了 10 倍。我发现现在更容易专注于前端应用程序的 UI/UX,而不必时刻关注整个后端状态。

为了将此库与 Redux 进行比较,可以查看代码中这两个方法的示例。我使用原生 JS、 React Hooksaxios实现了一个简单的 TODO 列表,并同时使用这两个方法从服务器获取数据

首先,Redux 的实现:

import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import axios from 'axios';

const SET_TODOS = "SET_TODOS";

export const rootReducer = (state = { todos: [] }, action) => {
  switch (action.type) {
    case SET_TODOS:
      return { ...state, todos: action.payload };
    default:
      return state;
  }
};

export const App = () => {
  const todos = useSelector((state) => state.todos);
  const dispatch = useDispatch();

  useEffect(() => {
    const fetchPosts = async () => {
      const { data } = await axios.get("/api/todos");
      dispatch({
        type: SET_TODOS,
        payload: data}
      );
    };

    fetchPosts();
  }, []);

  return (
    <ul>{todos.length > 0 && todos.map((todo) => <li>{todo.text}</li>)}</ul>
  );
};
Enter fullscreen mode Exit fullscreen mode

请注意,这甚至还没有开始处理重新获取、缓存和失效。它只是加载数据并在加载时将其存储在全局存储中。

以下是使用 React Query 实现的相同示例:

import React from "react";
import { useQuery } from "react-query";
import axios from "axios";

const fetchTodos = () => {
  const { data } = axios.get("/api/todos");
  return data;
};

const App = () => {
  const { data } = useQuery("todos", fetchTodos);

  return data ? (
    <ul>{data.length > 0 && data.map((todo) => <li>{todo.text}</li>)}</ul>
  ) : null;
};
Enter fullscreen mode Exit fullscreen mode

默认情况下,此示例包含数据重新获取、缓存和过期失效功能,并提供了相当合理的默认值。您可以在全局级别设置缓存配置,然后就不用管它了——通常情况下,它会按照您的预期执行。有关其底层工作原理的更多信息,请参阅React Query 文档。您可以使用大量的配置选项,这只是冰山一角。

现在,只要您需要这些数据,就可以使用useQuery 钩子,并设置唯一键(在本例中为"todos"),然后使用异步调用来获取数据。只要函数是异步的,具体实现方式就无关紧要——您可以轻松地使用Fetch API来代替 Axios。

为了改变我们的后端状态,React Query 提供了useMutation hook

我还编写了一个精选的 React Query 资源列表,您可以在此处找到。

驻波比

SWR在概念上与 React Query 几乎完全相同。React Query 和 SWR 大约在同一时期开发,并且彼此之间相互影响。react -query 文档中也对这两个库进行了详尽的比较。

与 React Query 一样,SWR 也拥有非常易读的文档。在大多数情况下,这两个库都不会出错。无论未来哪种库会成为主流,从 SWR 进行重构都比 Redux 的混乱局面容易得多。

Apollo 客户端

SWR 和 React Query 专注于 REST API,但如果你需要类似 GraphQL 的东西,那么领先的竞争者是Apollo Client。你会很高兴地了解到它的语法几乎与 React Query 相同。

前端状态怎么样?

一旦你开始使用这些库之一,你就会发现在绝大多数项目中,Redux 显得有些矫枉过正。当你的应用程序的数据获取/缓存部分已经处理完毕后,前端需要处理的全局状态就非常少了。剩下的少量状态可以通过ContextuseContext + useReducer来处理,从而创建你自己的伪 Redux。

或者更好的是,使用 React 的内置状态作为简单的前端状态。这样做本身并没有什么问题。

// clean, beautiful, and simple
const [state, setState] = useState();
Enter fullscreen mode Exit fullscreen mode

让我们更全面地拥抱前后端分离,而不是停留在这种模棱两可的中间状态。这些新兴库代表了我们在单页应用中管理状态方式的转变,是朝着正确方向迈出的一大步。我非常期待看到它们引领 React 社区走向何方。

文章来源:https://dev.to/g_abud/why-i-quit-redux-1knl
PREV
在任何 Web 框架中构建美观且适合移动设备的导航栏
NEXT
我希望多年前就知道的 Git 概念目录 Git 命令其他 Git 技巧 Git 工作流