最新发布《数智时代的AI人才粮仓模型解读白皮书(2024版)》,立即领取! 了解详情
写点什么

为什么我不再用 Redux 了

  • 2020-08-18
  • 本文字数:2838 字

    阅读完需:约 9 分钟

为什么我不再用Redux了

本文最初发布于 DEV 网站,经原作者授权由 InfoQ 中文站翻译并分享。


Redux 是 React 生态系统中的革命性技术。它使我们能够在全局范围内存储不可变数据,并解决了在组件树中 prop-drilling 的问题。需要在应用程序之间共享不可变数据时,它现在依旧是一种可以方便扩展的优秀工具。


但是,为什么我们非得需要一个全局存储呢?我们的前端应用程序真的那么复杂吗,还是说我们试图用 Redux 做的事情太多了?

单页应用程序的问题

React 这样的单页应用程序(SPA)的出现为我们开发 Web 应用程序的方式带来了许多变化。它将我们的后端与前端代码分离开来,使我们能够专心一致并分离出关注点。围绕状态,它还引入了很多复杂性。


现在,异步获取数据意味着数据必须位于两个位置:前端和后端。我们必须考虑如何在全局范围内以最佳方式存储这些数据,以便它们能对我们的所有组件都可用,同时保持数据缓存以减少网络延迟。现在,前端开发中的很大一部分负担来自于我们的全局存储的维护工作,我们还要确保这些存储不会遭受状态错误、数据非规范化和陈旧数据的困扰。

Redux 不是缓存

使用 Redux 和类似的状态管理库时,大多数人都会遇到的一大问题是,我们会将其视为后端状态的缓存。我们获取数据,通过 reducer/action 将其添加到存储中,并定期重新获取以确保它是最新的。我们用 Redux 做的事情太多了,甚至把它看成是解决问题的全面解决方案。


关键在于,我们的前端和后端状态永远不会真正同步,我们最多可以营造一种它们同步的错觉。这是客户端 - 服务器模型的缺点之一,也是为什么我们需要缓存的原因所在。但是,同步缓存和保持状态是非常复杂的,因此我们不应该像 Redux 鼓励的那样,从头开始重新创建这个后端状态。


当我们开始在前端重新创建数据库时,后端和前端之间的职责界限很快就变得模糊不清。作为前端开发人员,我们不需要完全了解表及其关系即可创建简单的 UI。我们也不必知道如何高水平地标准化我们的数据。这种责任应该落在设计表的那些人(后端开发人员)身上。然后,后端开发人员可以用文档化的 API 形式为前端开发人员提供抽象。


现在,人们围绕 Redux 构建了无数的库(redux-observable、redux-saga 和 redux-thunk 等),以帮助我们管理后端数据,每个库都为已经繁琐不已的库又增加了一层复杂性。我相信其中大多数都没有达成目标。有时为了前进。我们需要先退后一步。


如果我们不再在前端代码中管理后端状态,而只是将其视为需要定期更新的缓存会怎么样呢?将前端视为从缓存读取内容的简单显示层后,我们的代码就会变得更加易用,并且更适合纯前端开发人员阅读。我们获得了分离关注点的所有好处,同时避开了构建 SPA 的大部分缺点。

后端状态的更简单方法

我认为有两个库比使用 Redux(或类似的状态管理库)存储后端状态要好用很多。

React Query

我已经在自己的多数个人和工作项目中使用 React Query 几个月了。这个库有一个非常简单的 API 和几个 hooks,用于管理查询(获取数据)和突变(更改数据)。


自从使用 React Query 之后,我不仅提升了效率,而且最终编写的样板代码比 Redux 少了 9 成。我发现自己更容易将注意力集中在前端应用程序的 UI/UX 上,不会再时刻操心整个后端状态了。


要对比这个库和 Redux 的话,我们来看这两种方法的一个代码示例。我使用常规 JS、React Hooks 和 axios 实现了一个从服务器获取的简单 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>   );};
复制代码


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


下面是使用 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;};
复制代码


默认情况下,上面的示例包括具有合理默认值的数据重新获取、缓存和过时内容无效化。你可以在全局级别设置缓存配置,然后就可以忘掉它了——一般来说它足以完成你期望的工作。有关其幕后工作机制的更多信息,请查看 React Query 文档。它有大量的配置选项可用,本文只是介绍了一点皮毛。


现在,无论需要什么数据,你都可以将 useQuery hook 与你设置的唯一键(在本例中为“todos”)一起使用,并使用异步调用来获取数据。只要函数是异步的,实现就无关紧要——你可以轻松地使用 Fetch API 代替 Axios。


要更改后端状态时,React Query 提供了 useMutation hook


我还写了一份精选的 React Query 资源列表,你可以在这里浏览。

SWR

SWR 在概念上与 React Query 几乎一致。React Query 和 SWR 大约是在同一时间开始开发的,并且以积极的方式相互影响。在 react-query 文档中也对这两个库进行了彻底的比较。


与 React Query 一样,SWR 也有真正可读的文档


在大多数情况下,选择任何一个库都没什么问题。不管它们谁会在不久的将来成为事实规范,从它们中重构都要比 Redux 那堆乱麻要简单许多。

Apollo Client

SWR 和 React Query 专注于 REST API,但如果你在 GraphQL 上需要类似的东西,就可以考虑 Apollo Client。令人欣慰的是,它的语法与 React Query 几乎完全一样。

前端状态呢

一旦你开始使用这些库,就会发现在绝大多数项目中 Redux 都太笨重了。处理完应用程序的数据获取 / 缓存部分后,前端几乎没有全局状态可处理。可以使用 Context 或 useContext+useReducer 处理剩下的少量内容,代替 Redux 的作用。


或者更好的方法是,使用 React 的内置状态作为你的简单前端状态,这样做肯定没问题的。


// clean, beautiful, and simpleconst [state, setState] = useState();
复制代码


我们应该更彻底地分离后端与前端,而不是陷在这种模棱两可的中间状态里。本文提到的这些库代表了我们在单页应用程序中管理状态的方式变革,并且是朝着正确方向迈出的一大步。我期待着看到它们能对 React 社区产生怎样的影响。

英文原文

Why I Quit Redux


公众号推荐:

跳进 AI 的奇妙世界,一起探索未来工作的新风貌!想要深入了解 AI 如何成为产业创新的新引擎?好奇哪些城市正成为 AI 人才的新磁场?《中国生成式 AI 开发者洞察 2024》由 InfoQ 研究中心精心打造,为你深度解锁生成式 AI 领域的最新开发者动态。无论你是资深研发者,还是对生成式 AI 充满好奇的新手,这份报告都是你不可错过的知识宝典。欢迎大家扫码关注「AI前线」公众号,回复「开发者洞察」领取。

2020-08-18 09:113687
用户头像
小智 让所有人认同的文字称不上表达

发布了 408 篇内容, 共 377.4 次阅读, 收获喜欢 1972 次。

关注

评论

发布
暂无评论
发现更多内容

区块链溯源,茶叶溯源平台的搭建

13828808769

区块链+ #区块链#

一个魔幻的框架,3分钟纯 Java 注解搭个管理系统

程序员小富

Java 大前端 后端

实战案例丨分布式系统中如何用python实现Paxos

华为云开发者联盟

Python 算法 分布式系统 PAXOS 集群库

银四30天,苦心啃透java高级工程师面试1000题,涨薪10K很难吗?

Java 编程 程序员 架构 面试

JVM-技术专题-Class文件加载虚拟机

洛神灬殇

JVM Class字节码

云图说|一张图带你了解华为云分布式数据库中间件

华为云开发者联盟

数据库中间件 DDM 分布式数据库中间件 华为分布式数据库中间件

Java程序员都要懂得知识点:反射

华为云开发者联盟

Java 对象 反射 class 函数

区块链溯源,茶叶溯源平台的搭建

13828808769

#区块链#

python中find_element()和find_elements()的区别

Geek_6370d5

Python

Java 并发系列(二):DCL — Double Check Lock

TroyLiu

Java volatile 多线程 synchronized DCL

Python OpenCV setMouseCallback 回调函数,取经之旅第 13 天

梦想橡皮擦

3月日更

【技术面对面】基于场景图的多物体图像生成技术

京东科技开发者

云计算

实现一个“能中断”的ajax

云小梦

JavaScript ajax Promise axios 请求拦截

一次客户需求引发的K8s网络探究

京东科技开发者

云计算

LDO和DC-DC有什么不同?如何选型?

不脱发的程序猿

28天写作 3月日更 LDO DC-DC 电源转换

Rust从0到1-基础概念-注释

rust 代码注释

企业利用边缘计算的10种方式

浪潮云

边缘计算

【签约计划】技术编辑能力考核成绩公布

InfoQ写作社区官方

签约计划 热门活动

首站重庆聚焦智慧物流、呈现最新AI能力

百度大脑

AI 百度大脑

Python+OpenCV检测灯光亮点

不脱发的程序猿

Python OpenCV 28天写作 3月日更 检测灯光亮点

区块链溯源服务平台,区块链商品防伪溯源解决方案

13828808769

区块链+ #区块链#

智慧公安警务系统搭建,警务大数据可视化分析平台解决方案

13828808769

智慧城市

Mongodb特定场景性能数十倍提升优化实践(记一次十亿级mongodb核心集群雪崩故障)

杨亚洲(专注MongoDB及高性能中间件)

MySQL 数据库 mongodb 架构 分布式数据库mongodb

科技赋能城市建设,英特尔正式发布智慧社区解决方案参考架构

E科讯

开源项目月刊《HelloGitHub》第 60 期

HelloGitHub

GitHub 开源

Elasticsearch详细剖析

大数据技术指南

ES 3月日更

在开源的公链上实现隐私保护?静看NA公链 NAC公链创新之路应如何蜕变

区块链第一资讯

“广度”和“深度”,是我最终选择蚂蚁的理由

DT极客

Python基础之:Python中的异常和错误

程序那些事

Python Python3 程序那些事

百度飞桨中国行南京站开启!共研AI赋能产业新模式

百度大脑

百度 AI 飞桨

华为云自研PB级分布式时序数据库揭秘第一期初识GaussDB(for Influx)

华为云开发者联盟

云原生 时序数据库 华为云 分布式时序数据库 GaussDB(for Influx)

为什么我不再用Redux了_大前端_Gabriel Abud_InfoQ精选文章