鼓起勇气终于想说:真心不推荐使用 Redux - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hh54188
V2EX    分享发现

鼓起勇气终于想说:真心不推荐使用 Redux

  •  
  •   hh54188 2022 年 11 月 28 日 4266 次点击
    这是一个创建于 1234 天前的主题,其中的信息可能已经有所发展或是发生改变。

    原文在这里: Redux 的困扰与如何技术选型


    文章的名字我想了很久,备选项有“我再不推荐 Redux”,“Redux为什么令我头疼”,“Redux进化启示录”等等。通过这一系列名字我想你大概能猜到我接下来想聊的问题是什么,但这个问题放眼望去不是 Redux独有,而是在做技术决策时经常会遇到的,即使对于非前端背景的开发者也同样成立。最后决定用一个带有开放式标题也许能够引起更多的共鸣。

    这篇文章三年前( 2019 年)就想动笔,幸运的是当三年后再次拾起这个话题时,发现当初的观点依然成立。三年的时间 Redux的社区和 Flux的生态都有了巨大变化,这些变化可以作为我们讨论的补充

    如何在 Redux里发起异步请求

    如果你对 Redux还算熟悉的话,那么不妨回答这个问题:如何在 Redux里发起异步请求?我们暂且不管 Redux Toolkit (以下简称 RTK )的场景,把时钟拨回到三年前 RTK还未诞生(RTK 1.0发布于 2019年 10 月 23 日)前,用最核心的 Redux技术思考这个问题

    我这里给出一个备选答案看行不行?为了后面引用我们把这段代码称为方案 1:

    const mapDispatchToProps = (dispatch) => { return { fetchUser: () => { dispatch({ type: 'FETCH_START' }); fetch('https://randomuser.me/api/') .then(res => res.json()) .then(({ results }) => { dispatch({ type: 'FETCH_END', results, }); }) }, } } 

    在 Redux的官网教程中有专门一节来教授如何处理异步逻辑和数据抓取,无论是当下还是三年前(感谢web.archive )的教程,它推荐的都是借助 redux-thunk这个中间件来达到目的,这里我们称之为方案 2:

    function fetchUser() { return dispatch => { dispatch(requestUserStart()) return fetch(`https://randomuser.me/api/`) .then(respOnse=> response.json()) .then(json => dispatch(requestUserEnd(json))) } } 

    然而在围观过Dan Abramov( Redux和 React的核心成员)在 StackOverflow上解释为什么你应该使用 redux-thunk的回答("How to dispatch a Redux action with a timeout?""Why do we need middleware for async flow?")之后,你会发现方案 1 并非有什么大的过错,它可行,不过当用例复杂之后代码可维护性会下降。

    随之而来的是,如果你使用了 redux-thunk ,它提供的 getState方法你是否应该使用?在这个 StackOverflow上的回答(Accessing Redux state in an action creator?)里我们看到的是两位的官方维护者( Dan Abramov和Mark Erikson )的两种想左意见,Mark还专门有一篇长文解释这个问题(Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability

    Redux是模式,而非框架

    我们从上面看到了 Redux社区里的有趣现象,即是“原则”与“实践”的分离:redux-thunk作为官方出品并且推荐使用(但非必须,因为redux-saga 或者redux-promise 也能起到同样的效果)的中间件,并不在默认 Redux安装包中。同样的,Redux的官方文档中有大篇幅来谈immutable的重要性,或者是如何对数据进行normalizing, 但在工具上它却推荐使用第三方类库进行代码约束(主流的第三方插件在官方文档中的Ecosystem一页中都可以找到)

    与 getState的情况类似,在另一个“如何处理数据加载过程中报错”的问题中(What is the best way to deal with a fetch error in react redux?),Dan给出了他心目中的最佳实践。Dan的权威性让人很难不把他的回答当作为来自官方的建议。但是让人疑惑的地方在于,我们看到的有关 Redux的最佳实践来自于 StackOverflow上,而非官方文档中。

    为什么出现这样的结果?我的理解是 Redux从始至终并未被当作一款常规框架来设计,它绝不会指出完成工作的唯一方式。引用核心维护者 Mark Erikson的原话来解释就是:

    Redux is not intended to be the "most concise way of doing things", but rather to make data flow obvious and readable……Docs are written in a deliberately verbose style for clarity and learning, and not specifically intended as "the one true way to write Redux code",……Redux is a generic framework that provides a balance of just enough structure and just enough flexibility

    话虽如此,但我很难不认为他们自己也在动摇。上面的引用摘自在 Github上的一次关于 Redux 广泛讨论(这是一次很重要的讨论,有兴趣的同学建议读完):Request for Discussion: Redux "boilerplate", learning curve, abstraction, and opinionatedness。正如讨论的标题所示,以灵活性优先的设计理念,以及为学习而非实践服务的文档内容,给其他开发者带来了与我同身受的问题:高昂的学习成本、高度的抽象以及缺少对最佳实践的指导。

    这也是 Redux Toolkit诞生的原因,从 Mark对于 RTK的愿景(My Vision for Redux Starter Kit)中就不难看出它的目标在此:

    • Make it easier to get started with Redux
    • Simplify common tasks
    • Opinionated defaults guiding towards "best practices"
    • Provide solutions to make people stop using the word "boilerplate"

    RTK在当下已经作为 Redux项目的标配而存在了,在 Redux官网的第一章Getting Started with Redux 我们便会看到:Redux Toolkit is our official recommended approach for writing Redux logic“approach”是个有意思的词,它让我感觉原始的 Redux框架更像是摸不着的理论而存在而它的下半句Redux Toolkit builds in our suggested best practices, simplifies most Redux tasks, prevents common mistakes, and makes it easier to write Redux applications.将是我们聊技术选型的切入点。

    从 Redux到技术选型

    "prevent common mistakes"

    我反复向人推荐过一篇 StackOverflow联合创始人 Jeff Atwood的文章Falling Into The Pit of Success,简而言之它的中心思想是,好的系统设计应该很容易的让人们把事情做对,杜绝把事情做错。比如 type checking

    很显然 RTK之前的 Redux并不符合这一条件,甚至与之相悖。因为“容易做对”实践起来务必会削减框架提供的技术选项(甚至你最好只采用这一种方式去做),而 Redux主要目的是服务于灵活而非最佳实践。另一点是官方用大量段落的文字去讲述 immutable 、normalize 的重要性,但是在技术层面却不提供任何约束。

    而 RTK彻底解决了这个问题吗? RTK解决了代码模板的问题,天然集成了最佳实践,但它移除不掉高昂的学习成本,这是我最担心的。

    如果你有兴趣去拿 Redux文档与前流行的几个 Flux框架文档进行对比,比如MobxZustand 以及Akita,你会发现 Redux所需要掌握的概念和篇幅长度令人发指(很有意思 Angular是另一个极端,学习 Angular同样要掌握很多概念,但是对于你想做的每件事情它都帮你想到了,在文档中给出了最佳实践)。理解工程师热爱“挑战”,但是在现实中,团队是由不同水平的个体组成,团队所能接纳的“难度”常常不尽如人意。如果他们不理解他们的工作,那么他们就很难把工作做好。

    "simplifies most Redux tasks,easier to write Redux applications"

    Clojure的作者Rich Hickey在 2011年有一篇很有意思的演讲,名为“Simple Made Easy

    他给 easy和 simple做出了精确的定义

    • Simple:单个事物比如一件任务、一个角色、一个维度;简单的事物不应该包含交织的概念,比如一个实例,一次操作。simple是客观的
    • Easy:凡是我们熟悉的或者近在咫尺的事物都会让我们感到简单,比如说我们熟悉的语言,使用我们常用的 IDE ,easy是主观的

    easy可以优化你的启动速度,但如果你只是一味的追求 easy而忽略了复杂性的话长远看还是事倍功半的。比如在 Flux之前的MVC时代,在 model中去更新 view或者在 view中直接调用 model是多么 easy的事情,但这却让状态管理的复杂性大增。

    Redux算 easy吗?不,从 Flux到 Redux是一整套全新的概念;它算 simple吗?未必,在你设计 reducer的时候你很难不去思考 normalize

    流行

    “流行”或者“标配”不应该是技术选型的参考之一。

    你的前端项目需要 Redux吗?你也许会用反问来回答我这个问题:Redux不是 React项目的标配吗?

    并非如此。无论是在 Hacker News上网友对于 Redux的抱怨(God I hate redux)里,还是在reddit上在对 Redux的仇恨讨论中(Why all the sudden hate for Redux?),Mark都解释到团队从来都没有以 Redux作为 React的主流状态管理工具去营销它,它之所以变得主流一方面是因为它赢得了 2015年的Flux Wars ,另一方面它切实解决了开发者的问题。Dan也写过一篇文章来刻意强调你也许并不需要 Redux (You Might Not Need Redux)。

    如果说我们把 2015年的 Flux War比喻成第一次 Flux世界大战的话,那么当下这个时间点第二次世界大战正进行的如火如荼,在市面上我们可以看到更多优秀的 Flux框架,比如Mobx,比如冉冉升起的Zustand,比如小众的我很喜欢的Akita。Redux很大可能不会是你下一个新项目的最佳选项

    在 StafkOverflow直白的发文求网友推荐技术框架一类的问题通常都会被关闭。因为在没有任何上下文的前提大部分答案过于主观了。如何思考我建议你至少参考这个回答(How can Stack Overflow help developers evaluate technologies?),先问问自己:

    • 我需要什么( Know what you need )
    • 我不需要什么( Know what you don't want )
    • 我还想要些什么( Know what you want )

    但我可以理解“简历驱动开发”是选择当下的流行技术的重要原因,我也深表同情。

    12 条回复    2022-11-30 14:12:22 +08:00
    mxT52CRuqR6o5
        1
    mxT52CRuqR6o5  
       2022 年 11 月 28 日   1
    redux 的精华全在 devtools 上了,redux 设计的那些冗余的概念都是为了让 devtools 更好用,用 redux 必须要学会用 devtools ,不然妥妥的负收益
    emric
        2
    emric  
       2022 年 11 月 28 日   1
    但是对于团队来说 redux 这种规规矩矩,反而可以让水平参差不齐的人写出 “可读” “可用” 代码最有效的办法。
    个人项目来说 zustand 真的好爽。
    wu67
        3
    wu67  
       2022 年 11 月 28 日
    我还是喜欢 recoil
    gouflv
        4
    gouflv  
       2022 年 11 月 28 日 via iPhone
    redux 是哪一年出的还有人记得吗,
    KevinDo2
        5
    KevinDo2  
       2022 年 11 月 29 日 via iPhone
    我还是喜欢 zustand ,就一个字,爽。
    存什么,怎么存,写好后,一个 hook 拿来用即可。
    ktqFDx9m2Bvfq3y4
        6
    ktqFDx9m2Bvfq3y4  
       2022 年 11 月 29 日 via iPhone   1
    使用 angular

    不知道 redux 是干什么的以及为什么需要它
    shengchao
        7
    shengchao  
       2022 年 11 月 29 日
    字好多,就看了个开头
    justin2018
        8
    justin2018  
       2022 年 11 月 29 日
    Redux 长时间不用就忘记咋用了

    目前用 zustand

    要求不高 能跑 就行
    xieren58
        9
    xieren58  
       2022 年 11 月 29 日
    真心不推荐 react. solidjs 好玩多了.
    devtiange
        10
    devtiange  
       2022 年 11 月 30 日
    Dan 刚做出 redux 的时候, 舔 Jing Chen 舔的那个认真啊
    Rocketer
        11
    Rocketer  
       2022 年 11 月 30 日
    Angular 的 service 真香,react 有没有现成的解决方案使用 service 呢?
    stimw
        12
    stimw  
       2022 年 11 月 30 日
    React Query!
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     908 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 95ms UTC 19:58 PVG 03:58 LAX 12:58 JFK 15:58
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86