Redux 死了吗?

2025-05-24

Redux 死了吗?

作者:Kristofer Selbekk

React 首次发布时就彻底改变了大多数人所熟知的前端开发方式。这种全新的代码编写方式在处理状态变化和 UI 更新方面引发了令人难以置信的创新。

这场革命也有其弊端。其中之一就是过度设计解决方案的文化,而这些解决方案本可以用更简单的方法解决。一个典型的例子就是 React 应用程序中的状态管理方式。

Redux 已经成为近几年来许多 React 应用的标志性特征。在应用程序中随处可见的单一状态对象听起来确实很棒。但它的时代已经过去了吗?React 是否已经发展到这种地步,以至于这类状态管理工具带来的复杂性远大于它们所解决的问题?

本文旨在帮助您深入了解哪些情况需要使用像 Redux 这样的状态管理工具。我们将讨论 Redux 崛起的原因,以及过去几年 React 和 Redux 发生的变化。最后,我们将展望未来可能的发展方向。

Redux - 以及人们开始使用它的原因

React 刚发布时,并没有官方支持的方式将数据传递到组件树的深处。如果你想在应用程序中的任何地方使用某种共享状态、配置或其他信息,就必须将其从父级传递到子级,再从兄弟级传递到下一个子级。虽然有一种方法可以避免这种情况,但“遗留上下文 API”从未得到官方支持,并且在文档中警告不要使用它。

在 React 正式发布的同时,其他几位 Facebook 工程师也提出了前端应用开发蓝图——Flux架构。它通过单向数据流,补充了 React 以组件为中心的设计理念,使代码易于理解和跟进。

Flux 架构
(图片取自https://facebook.github.io/flux/docs/in-depth-overview

当许多著名的开放魔法师忙于争论哪种略有不同的实现才是最好的时,一位名叫 Dan Abramov 的年轻俄罗斯开发人员推出了一种基于Elm 架构的实现,称为 Redux。

https://youtu.be/xsSnOQynTHs

Redux 是一个非常简单的系统,只有一个状态对象,封装在一个“store”中,可以通过在其上调度 action 来更新。这些 action 被发送到一个“reducer”函数,该函数返回整个应用程序状态的全新副本,然后该副本会在整个应用程序中传播。

Redux 的另一个很棒的特性是它与 React 的易用性。它不仅与 React 的编程模型完美契合,还解决了 prop 钻取问题!只需将你想要的任何组件“连接”到 store,你就可以访问应用程序状态的任何部分。这简直就像魔法一样!

上下文、钩子以及它为什么解决了 Redux 的大部分问题

尽管 Redux 优雅且广受欢迎,但它也有一些重大缺陷。对于每一种新的状态更改方式,你都必须添加一个新的 action 类型和 action 创建器(可能还需要一个 dispatcher 和一个 selector),然后你还必须在现有的 reducer 中处理新的状态更改,或者创建一个新的 reducer。换句话说,你需要大量的样板代码。

React 16.3 版本发布时,终于带来了全新设计的 context API。有了这个新特性,prop 钻取变得非常简单,只需将应用程序的任何子部分包装到 context 提供程序中,然后使用 context 消费者组件再次获取即可。以下是具体操作示例:

const UserContext = React.createContext();
class MyApp extends React.Component {
  state = { user: null };
  componentDidMount() {
    myApi.getUser()
      .then(user => this.setState({ user }));
  }
  render() {
    return (
      <UserContext.Provider value={this.state.user}>
        <SomeDeepHierarchy />
      </UserContext.Provider>
    );
  }
};
const UserGreeting = () => {
  return (
    <UserContext.Consumer>
      {user => ( // look - no Redux required!
        <p>Hello there, {user.name || 'customer'}!</p>
      )}
    </UserContext.Consumer>
  );
};
Enter fullscreen mode Exit fullscreen mode

在 2018 年的 ReactConf 大会上,React Core 团队成员 Dan Abramov 和负责人 Sophie Alpert介绍了React 的一项新功能——Hooks。Hooks 让状态和副作用的使用变得更加简单,并且彻底消除了对类组件的需求。此外,context API 的使用也变得异常简单,更加用户友好。以下是使用 Hooks 修改后的代码示例:

const UserContext = React.createContext();
const useUser = () => {
  const [user, setUser] = React.useState(null);
  React.useEffect(() => {
    myApi.getUser().then((user) => setUser(user));
  }, []);
}
const MyApp = () => {
  const user = useUser();
  return (
    <UserContext.Provider value={user}>
      <SomeDeepHierarchy />
    </UserContext.Provider>
  );
};
const UserGreeting = () => {
  const user = React.useContext(UserContext);
  return <p>Hello there, {user?.name ?? "customer"}!</p>;
};
Enter fullscreen mode Exit fullscreen mode

随着这些新功能在 React 中的推出,使用 Redux 的利弊发生了很大变化。Reducer 的优雅性突然被内置到 React 中,prop-drilling 的难题也迎刃而解。新项目开始不再依赖 Redux——这在以前是理所当然的——越来越多的项目开始考虑彻底放弃 Redux。

Redux Toolkit 和 hooks——全新改进的用户体验?

为了应对这种情况,目前维护 Redux 的团队(由一位名叫 Mark Erikson 的先生领导)开始了两项不同的工作。他们推出了一个名为Redux Toolkit的工具包,该工具包通过约定消除了大多数样板代码,并添加了一个基于 hooks 的 API用于读取状态和调度操作。

这两个更新加在一起,大大简化了 Redux 的代码库。但这真的足以让我们在新项目中引入 Redux 中新增的复杂概念吗?Redux 带来的价值,是否超过了向新员工讲解 Yet Another Tool 所增加的成本?

让我们看看 React 本身在哪些方面表现出色,以及在什么情况下复杂性与功能性的权衡是值得的。

当 React 足够时

我接触过的大多数 React 应用规模都很小。它们只有一些在整个应用范围内使用的全局状态,以及一些在多个不同视图之间共享的数据。

除此之外,许多 React 应用并没有太多共享状态。大多数状态,例如输入框的内容或模态框是否打开,只有包含它们的组件才需要!没有必要将这些状态设置为全局可用。

其他状态可能被共享,但仅限于应用程序的一部分。也许某个页面需要在其多个组件之间共享某个状态,或者侧边栏需要向其所有子组件公开某个远程状态。无论哪种情况,这都不是全局状态——而是作用域限定于应用程序某一部分的状态。

通过保持状态共置或尽可能靠近其依赖项,您可以确保在需要它的功能被删除时它也会被删除,并且无需翻阅数十个不同的减速器即可发现它。

如果你需要共享很少更改的应用级设置,React 的 context API 是一个很棒的工具。例如,当前处于活动状态的语言环境:

const LocaleContext = React.createContext({
  locale: "en-US",
  setLocale: () => {},
});
const LocaleProvider = (props) => {
  const [locale, setLocale] = React.useState("en-US");
  return <LocaleContext.Provider value={{ locale, setLocale }} {...props} />;
};
const useLocale = () => React.useContext(LocaleContext);
Enter fullscreen mode Exit fullscreen mode

其他用例可以是激活哪种颜色主题,甚至是针对特定用户激活哪种实验。

另一个非常有用的方法是使用像SWRReact-Query这样的小型数据获取库来处理 API 响应的获取和缓存。对我来说,缓存数据并非真正的全局状态,它只是缓存数据而已。使用这些小型的一次性库比在 Redux 中引入异步 thunk 或 sagas 要简单得多。此外,你不必处理 isLoading、hasError 等等各种复杂的情况。有了这些库,一切开箱即用。

这些上下文用例的共同点是,它们代表的数据很少更新。在计算机科学的语境中,“很少”这个词可能有点模糊,但在我看来,每秒更新少于几次就已经很罕见了。事实证明,这正是 React Context API 的最佳工作方式!

上面总结的用例涵盖了我在实际应用中遇到的大多数情况。实际的全局状态很少见,通常最好将其与实际使用它的代码放在一起,或者通过 context API 提供。

Redux 适用的情况

尽管如此,Redux 仍然是一款出色的产品。它拥有完善的文档,被广泛采用,并且可以与上述方法结合使用。但是,在 2021 年,哪些用例值得将 Redux 添加到你的技术栈中,这会增加复杂性和学习曲线呢?

在我参与的项目中,最常见的用例之一是需要大量级联网络通信的高级数据获取场景。有人可能会说,这最好在服务器端完成,但确实存在一些用例,需要将其交给客户端处理。Redux,尤其是与所谓的 thunk 结合使用时,在此类编排方面极其灵活多用。

另一个用例是高度相互依赖的状态,或者由多个其他状态派生的状态。在 React 中也可以处理这种情况,但最终结果在 Redux 中仍然更容易共享、复用和推理。

第三种用例是针对应用程序状态可能快速变化的情况。React 首席架构师 Seb Markbåge 几年前曾指出,当前的 context API 实现对于共享快速更新的数据而言并非最佳,因为 context 提供的值发生更改会触发整个组件子树的重新渲染。Web 套接字驱动的交易或分析仪表板可能是这种情况的典型示例。Redux 通过仅通过 context 共享 store 实例来解决这个问题,并更明确地触发重新渲染。

最后一个用例非常主观,适合喜欢自上而下单状态树方法的团队。应用程序的整个状态可以序列化、反序列化、通过网络发送并持久化到本地存储。您可以跨变更进行时间旅行,并向错误跟踪工具提供导致错误的完整操作故事。这些都是强有力的论据,对某些团队来说绝对是一种增值。

测量 Redux 性能

在生产环境中监控 Web 应用的性能可能既困难又耗时。Asayer 是一款前端监控工具,可以重放用户的所有操作,并展示应用在遇到每个问题时的表现。这就像打开浏览器的检查器,同时监控用户的运行情况。

Asayer 可让您重现问题、汇总 JS 错误并监控应用性能。Asayer 提供用于捕获Redux 或 VueX存储状态以及检查Fetch请求和GraphQL查询的插件。

Asayer Redux

对于现代前端团队来说,快乐调试 -开始免费监控您的网络应用程序

其他选项

在我看来,大多数应用程序无需外部状态管理库即可运行。有些人不这么认为,而且有些应用程序的用例非常复杂,以至于不使用某种中间层来处理状态管理非常不切实际。在这种情况下,我建议您先了解一下 Redux 的竞争对手,然后再选择其他久经考验的替代方案。

MobX是一款久经考验且广受欢迎的状态管理工具,它通过可观察对象的魔力来实现。它运行速度极快,大多数尝试过的人都会在几周内成为它的粉丝。我自己还没有尝试过,所以不会太推荐它,但它的设计看起来很扎实!
另一个值得一提的库是Recoil。这个库也源自 Facebook 的工程师,基于状态原子的概念以及称为选择器的派生状态。它的 API 设计与 React 非常相似,并且能够完美地协同工作。它目前处于公开测试阶段,但在许多项目中应该仍然有用。

我最后想推荐的替代方案是Overmind。Overmind是CodeSandbox上运行主编辑器应用程序的状态库,它基于单状态树和副作用。这也是我从未尝试过的东西,但考虑到 CodeSandbox 的复杂性和几乎没有 bug,它一定非常强大!

即使存在所有这些替代方案,Redux 仍然保持着自己的地位。随着最近添加的 hooks 和 Redux Toolkit,开发人员的体验也得到了真正的提升。

概括

React 是一个出色的框架,可用于创建快速、响应迅速且优化的用户界面。它提供了灵活的 API 来处理简单和复杂的状态,最新版本的 React 极大地提升了开发者体验,以至于大多数状态管理库都不再需要了。

在某些用例中,单独的状态管理层确实会带来好处,因此在需要时,你应该始终考虑引入它。我的观点是,在你感受到没有状态管理层的痛苦之前,不应该一开始就引入它。只有这样,你才能确保不会在不获得任何好处的情况下增加堆栈的复杂性。

文章来源:https://dev.to/asayerio_techblog/is-redux-dead-1d2a
PREV
Redux 已死:Redux 工具包万岁
NEXT
成为更好的开发人员必须了解的重要 Javascript 函数