Redux: 讨论请求:Redux“样板”、学习曲线、抽象和自以为是

创建于 2017-03-19  ·  108评论  ·  资料来源: reduxjs/redux

解决方案:使用 Redux Starter Kit

这个帖子中的想法最终变成了我们新的Redux Starter Kit 包。 它包括用于简化许多常见 Redux 用例的实用程序,包括存储设置、reducer 定义、不可变更新逻辑,甚至无需手动编写任何操作创建器或操作类型即可自动创建整个状态“切片”。

有关 Redux Starter Kit 旨在解决哪些问题(以及它不会做什么)的更多详细信息,请参阅我写的“Vision for Redux Starter Kit”宣言

我看到的关于 Redux 的第一个抱怨是“样板太多”。 我也经常看到抱怨说有太多东西需要学习,需要太多其他插件来做任何有用的事情,以及 Redux 没有任何意见的太多方面,因此不提供任何类型的内置指导。

我刚刚与@tannerlinsley就其中几个方面进行了讨论。 我们讨论了Jumpstate库,它是一个围绕 Redux 的抽象层,它是如何简化学习曲线的,以及关于什么构成“好的”Redux 使用的各种哲学观点。

因此,我们提出了几个问题,我想提出更广泛的讨论:

关键点

样板/冗长

  • Redux 并不是要成为“最简洁的做事方式”,而是要让数据流清晰易读
  • 为了清晰和学习,文档以一种故意冗长的风格编写,并没有特别打算作为“编写 Redux 代码的一种真正方式”,但这种风格在某种程度上被盲目采用(或者,有时,导致拒绝 Redux)
  • 经常抱怨“样板文件”,例如“文件太多”、“使用动作创建者”等

抽象与学习

  • 核心 Redux 库本身实际上功能齐全,但社区正在构建许多有趣的插件和工具
  • 已经构建了各种抽象库来“缓解学习曲线”或“使事情更加 OOP”,但其中大多数并不是真正“惯用的”Redux 用法
  • Redux 学习曲线可能很陡峭,但是一旦掌握了概念,对抽象层的需求通常就会消失

问题

  • “样板”投诉主要是关于什么?
  • 对于新手来说,Redux 最难的方面是什么?
  • 给人们带来问题的“无意见”领域是什么?

潜在的解决方案

  • 带有“较少样板”的惯用 Redux 用法会是什么样的? 我们如何为这些投诉提供解决方案?
  • 可以创建哪些可能的抽象来简化学习和使用过程,但又不会真正隐藏 Redux(并希望提供一个迁移/学习路径到“基础”Redux)?
  • 有多少可以通过改进文档以某种方式解决?

我想邀请社区提供关于使用 Redux 的抱怨、痛点和担忧,也希望也提供解决这些问题的建议和想法。

最有用的评论

如果由我决定,我想看看这个:

  • 一个 Flow/TypeScript 友好的子集,比普通 Redux 更容易输入。
  • 不太强调常量(只是使使用字符串文字更安全)。
  • 保持独立于 React 或其他视图库,但更容易使用现有绑定(例如提供mapStateToProps实现)。
  • 保留 Redux 原则(可序列化的动作、时间旅行和热重载应该有效,动作日志应该有意义)。
  • 以更直接的方式支持代码拆分。
  • 鼓励将 reducer 与选择器并置在一起,并减少它们一起编写的尴尬(想想易于编写和组合的 reducer-selector 包)。
  • 而不是将动作创建器与减速器并置,完全摆脱动作创建器,并使动作到减速器的多对多映射变得自然(而不是像大多数图书馆那样回避它)。
  • 设置合理的性能默认值,因此通过 Reselect 进行记忆“仅适用于”常见用例,而无需用户编写该代码。
  • 包含用于索引、规范化、集合和乐观更新的内置帮助程序。
  • 具有内置的可测试异步流支持。

当然,在核心之上,尽管将其标记为正式的 Redux 东西也不错。
另外,我不会写这个。 但是你可以。

所有108条评论

一些想法:

  • 包含 redux、react-redux、redux-thunk 的官方 redux-preset 包已经连接在一起
  • dispatch(actionType, payload)可以减少对动作创建者的需求
  • 内置跳转状态风格的减速器创建者,例如createReducer({[actionType]: (state, payload) => state}, initState)

我认识的大多数人都大量使用 redux,最终放弃了 redux-thunk,因为他们发现它不能很好地扩展,最终导致动作创建者开始做太多事情,而是使用其他东西来管理方面- 效果如 redux-observable 或 redux-saga。

我可以理解其中的吸引力,尤其是在开始使用 redux 时 - 但我不确定将它捆绑为“最佳实践样板包”的一部分是否是最好的主意。

所以,这部分对我来说更重要(这不是解决问题的想法,而是至少对我来说很重要的约束):

Redux 并不是要成为“最简洁的做事方式”,而是要让数据流清晰易读

Redux 的一大优点是它几乎更像是一种设计模式而不是框架,并且您看到的所有代码(除了 store 和react-redux )都是您自己的。

看看 Jumpstate(直到现在我才知道它),它是我看到的第一个看起来不错的 Redux 抽象层之一。 甚至很棒! 但同时,它使您与 MobX 等其他框架仅差一点点,并且拥有多个框架来提供相同的内容并不是很有用。 因此,关注 Redux 与其他工具的不同之处很重要,而不仅仅是我们如何在真空中使 Redux 变得更好(因为它与其他工具生活在同一个世界中)。

另一件重要的事情是,新手在使用 Redux 时遇到的很多挑战不是 Redux 本身,而是 JavaScript。 Angular 等其他大型框架抽象了 JavaScript 本身。 编写一个 Redux reducer 就是简单地应用原生的 javascript “设计模式”,这对于新手来说可能并不熟悉,并且会让人们想要使用黑盒来代替。

最后,许多样板化简库尝试在组件、reducer 和动作创建者之间添加 1:1:1 的关系(可能不是全部 3 个,但通常是其中的 2 个),这让新手忘记了他们可以有多个 reducer 来处理一个动作,多个组件使用来自多个减速器的状态,等等。 这是我在工作和其他地方回答的第一个问题之一,它非常有用。 因此,在该领域提供帮助的工具不应该失去这一点(此外,“开关很讨厌”并不是世界上最好的论点)。

因此,IMO、CTA 将涉及链接优秀的 JavaScript 资源,以及沿着“入门”概念记录更多“为什么”,并保持简单。 您可以在 Redux 中构建巨大的应用程序,而无需学习许多新概念。 虽然我自己是一个 redux-observable 人,但我已经看到数十万行代码应用程序使用 Thunk 没有问题(并且我已经看到小型应用程序在使用 thunk 时出问题了)。 介绍很少的“核心”概念并展示如何将它们应用于大量概念会有很大帮助。

“无论成本如何,都必须减少所有样板文件”是当今整个软件工程社区的一个问题……这更难解决。

从一开始,我对 redux 的主要担忧是,无论是读还是写代码,我都必须在 N 个文件之间跳转,单个 UI 部分的 b/c 逻辑散布在动作、动作类型和多个动作之间的代码库中减速器。 我真的很喜欢我可以从 UI 中的每个位置访问状态树的任何部分,并且我可以响应单个操作来更改状态的不同部分(我使用 redux 的主要原因),但是更多的部分我响应单个动作而改变的状态,我的逻辑越模糊。 当用户这样做或那样做时,我不能简单地读或写发生了什么。 但我想要的是可以这样描述:

// meta code
dispatch(ACTION);

onAction = {
  ACTION: [
    // handler 1: hide spinner here,
    // handler 2: change status there,
    // handler 3: update entity
  ],
};

最后我想出了redux-interactions + redux-tree并且到目前为止我很高兴。

我们当前项目中的方法:

注意,根据应用程序的要求会有很大不同:) 一旦我们添加了实时对象更新,也许 saga 或 observables 会比 thunk/promise 提供好处

  • redux-thunk
  • 承诺中间件
  • 我们的“api 包装器”,它是客户端羽毛应用程序的包装器, service(name, method, ...opts)是一个动作创建者,其有效载荷是调用我们的 API 的承诺(即承诺中间件拾取并分派x_FULFILLEDx_PENDINGx_REJECTED )。 也可以为此覆盖操作名称。

我们首先使用componentDidMount()来调用我们的 API,将数据存储在组件状态。 但是,我们为此使用了 api 包装器,这意味着仍然会发出一个操作(并由记录器中间件记录,包括作为请求信息的元数据),如果我们希望重构为使用该组件的存储,我们只需要to 是添加一个侦听动作的减速器。 我们从仅使用本地状态开始,并在需要从另一个组件访问/修改该组件的状态时重构为 redux。 话虽如此,我看到了在任何地方使用 redux 的吸引力,因为它为所有内容提供了书面记录。 鉴于我们目前的团队和申请时间表,这对我们 atm 没有好处。

我玩过 redux-saga 一点,但是由于我们最复杂的异步流程是登录/注销,它可以工作(并且代码不是非常复杂),所以切换的理由不是很大(但可能值得测试原因 - 可迭代 + 模拟是一种很好的测试方式)。 redux-observable 我没有看到直接的好处,除非 observables 本身会提供好处,例如,如果你想很好地双击事件。

通过拥有我们自己的工厂函数来返回减速器,并在自定义功能之上拥有我们自己的高阶减速器(例如,在“分页器”之上的减速器,以便内容分页但可以自定义),我们已经有点远离样板了修改项目的操作)。

我认为需要做的是一个巨大的教程,从一个基本的 React 应用程序开始,演示组件间通信出现的问题,然后介绍 redux,然后继续,演示在特定情况下发生的问题,以及它们如何得到帮助 redux-x 。 也就是说,何时使用以及使用什么的指南。 肯定有一些博客文章存在这个方向的讨论。

同样相关的是我为gothinkster/react-redux-realworld-example-app 中使用的模式提出的总结

我认识的大多数大量使用 redux 的人最终都放弃了 redux-thunk,因为他们发现它不能很好地扩展

我明白你在说什么,但这是我的另一面担忧:我们看到越来越多的轶事证据表明,一大群 JS 开发人员甚至还没有使用 _arrow 函数_ 和其他基线 ES(6|2015) 功能,由于缺乏理解,恐吓等。期待那些想要开始使用 Redux 并且可以从学习 Redux 引入的 _patterns_ 中受益的人,首先学习 observables 或 generators 我认为可能要求太多了吗?

Redux-thunk 也有点复杂,因为 redux 中间件通常会让你的大脑弯曲,但它至少只是函数/回调,如果你正在编写 JS,它们很容易上手。 我真的很喜欢拥有完整的相关工具包来开始使用的想法,可以通过一次下载获得,即使它被定位为“learn-redux”而不是“best-practice-always-redux”。 类似于 create-react-app 在你学习它为你设置的所有东西时需要调整的方式,这可以鼓励你自己的调整,也许展示如何将简单的 redux-thunk 设置转换为使用 sagas 等作为它自己的形式“弹出”...

只是一些想法。

我明白你在说什么,但这是我的另一面担忧:我们看到越来越多的轶事证据表明,大量 JS 开发人员甚至还没有使用箭头函数和其他基线 ES(6|2015) 功能,由于缺乏理解,恐吓等。期待那些想要开始使用 Redux 并且可以从学习 Redux 引入的模式中受益的人,首先学习 observables 或 generators 我认为可能要求太多了吗?

答对了! 在我们所处的环境中,很多人都不熟悉 JS(或者他们“知道”JS,但这不是他们的专长,而且他们是从最底层开始的)。 特别是如果这些人是来自另一个生态系统(java、rails 等)的经验丰富的软件工程师,他们会在学习他们不知道的概念之前快速尝试并应用他们知道的概念,但它不会完全奏效,他们会陷入困境。 我不知道在跳入功能性 UX 设计模式之前,说服人们深入了解 JS 的最佳方法是什么。

请注意,Redux 文档确实有一个关于减少样板文件的部分,供您阅读的那些正在寻找某些东西并且不想在 Redux 之上采用整个库的人使用。

我们应该对这个讨论小心一点,并意识到这可能已经被很多人抛弃了。 Redux 团队已经听说、考虑并拒绝了很多可能会在这里提出的事情。 如果我们只讨论已经讨论过的事情,很可能不会有任何结果。

话虽如此,我认为谈论使每个人(新手和有经验的人)更容易使用该框架的方法是一个好主意。

您建议减少样板文件的任何内容都应该可以在需要时返回到抽象下方的内容。 采用一个抽象只是为了稍后放弃它返回到较低级别的东西,这并不有趣,因为您需要一个抽象的作者没有想到的额外东西。

Redux 学习曲线可能很陡峭,但是一旦你掌握了概念,就需要
抽象层经常消失

我想知道,如果是这种情况,我们建议改进 Redux 的哪些部分? 您会删除或抽象哪些部分不需要立即重新添加?

这是大约一年前我在工作中的一次演讲中制作的整个 react/redux 生命周期的图表:
React Redux Lifecycle

这有很多部分,但我无法想象 Redux 在没有其中任何一个的情况下也能正常工作。 容器有时有点烦人,但如果你删除它们,你的视图逻辑和数据布局就会深深地耦合在一起。 如果您删除操作,您基本上又是 MVC,这首先忽略了使用 Redux 的要点。 该图中的“视图”几乎不存在,因为它可以被建模为一个订阅 store 并呈现 react 组件的函数。

我不认为在整个框架本身中有很多样板需要删除。 也许您指的是框架的各个部分中的样板,例如动作创建器、减速器或容器。 在这种情况下,上面提到的减少样板页面的提示已经解决了大部分问题。 除此之外,我们不需要任何东西来让它变得“更好”。 (注意:并不是说我不愿意添加一些东西来让事情变得更好,只是我还没有看到。)

也许这与其说是减少样板问题,不如说是改善 Redux 教育的问题。 如果初学者难以理解该框架,我们可能需要改进 Redux 文档并使其更易于访问。 也许需要在文档中更积极地宣传一些减少样板提示的技巧。

减少所需的步骤(样板)并不总能解决问题。 为了从我的帖子一开始就强调我的观点,你不想写一个会被扔掉的抽象,因为你没有想到人们需要使用它的所有方式。

到目前为止一些很好的讨论。 Lemme 抛出一些我看到的常见“样板”相关投诉的快速示例:

  • “为什么我必须编写 'action creator' 函数来立即返回一个对象?”
  • “为了添加一个简单的新功能,我不得不接触很多文件!此外,我不断为常量和函数名称重复相同的名称,并且......”
  • “为什么我需要这些‘中间件’来进行 AJAX 调用?”
  • “为什么我必须对所有事情都使用 switch 语句?”
  • “为什么我需要这个dispatch东西?为什么我必须把所有这些功能都打包起来才能让它们工作?”

以及这些类型评论的一些具体示例:

并立即抛出对那些“样板”问题的一些回应:在列出的这五个类别中,只有“使用dispatch ”实际上是_必需的_。 对于其余的:

  • 您不必_必须_使用动作创建器,但这是保持一致性的好做法(根据我的帖子Idiomatic Redux:为什么使用动作创建器?
  • 您不必_必须_为动作创建者、动作常量和减速器提供完全独立的文件。 “鸭子”模式是将所有这些放在一个文件中的流行方法。 这确实有“隐藏”让多个减速器听一个动作的能力的缺点。 Redux 最终并不关心你的文件结构是什么。
  • 您_可以_完全在 Redux 之外进行异步工作,就像在组件中一样。 但是,根据 Dan 在Why we need middleware for async flow in Redux 中的描述? ,在组件之外提取逻辑通常是好的,中间件为在访问 Redux 存储的同时执行异步工作提供了一个“漏洞”。
  • 你绝对_不_必须在减速器中使用 switch 语句,它们只是处理给定变量的多个值的最“明显”的方式。 查找表、if/else 语句以及您想要的任何其他内容都可以(根据常见问题解答:我必须使用 switch 语句吗?
  • 你_do_需要实际调用store.dispatch()来使任何有用的事情发生 - 这是 Redux 的基本设计决策。 绑定动作创建者可以将它们传递给未连接的子组件,并且在调用函数时仍然让它们分派。

所以总的来说,这些“样板”问题几乎没有_必需_。 它结合了文档中的示例和“良好的编程实践”,例如去重代码和关注点分离。

我认为@markerikson提出的问题非常公平,我过去也曾问过自己。

Redux 从某种意义上说是一个用于数据建模的“低级”库。 像任何这样的低级库一样,它公开了很多你可以很容易地抽象出来的东西,以便解释大多数情况。 我认为@gaearon最初没有这样做的原因是他想让库尽可能小而灵活。 由于这个决定,我们能够在 Redux 之上构建许多不同的东西,而无需在 Redux 核心中拥有所有东西。

也许我们应该考虑正确的道路可能是在 Redux 之上开发和稳定一个好的库(比如 Jumpstate?)。 我们从教人们开始,然后为他们提供一条简单的途径,让他们在需要时直接使用 Redux。

我不认为 Redux 在其核心代码库中需要更多的东西,而且我没有看到它的任何部分需要永久地抽象掉。 (如果你这样做了,请告诉我 :smile:) 在我看来,在 Redux 核心中添加或删除东西并没有什么好处。

将库改进到足够稳定和灵活供每个人使用的程度可能是更好的选择。 就像你说的,实际上并不需要太多的“样板”,所以让我们在 Redux 之上的库中摆脱它,而不是修改 Redux 本身。

在另一个社区中发生这种情况的一个例子是 Rust 编程语言。 Rust 编程语言有一个非阻塞 IO 库,称为“mio” 。 它专注于像 Redux 一样小而低级。 问题是,几乎没有人直接使用它,因为那会非常困难并且充满样板。 大多数人都使用另一个名为tokio 的库,它建立在 mio 之上,使其非常实用且符合人体工程学。 这样,任何需要 mio 管道的人都可以直接使用它,但任何只想快速制作某些东西的人都可以使用 tokio。

我们应该采用类似的模式。

要扩展@sunjay的一些评论:

最近在#775 中有一条评论,我认为它很好地捕捉了事物:

Redux 是一个通用框架,它提供了恰到好处的结构和恰到好处的灵活性之间的平衡。 因此,它为开发人员提供了一个平台,可以为其用例构建自定义状态管理,同时能够重用图形调试器或中间件等内容。

所以是的,Redux 在很多方面都“只是一种模式”。 核心库确实是功能完备的——唯一真正的半计划更改是提议的增强器重组(#1702、#2214),并可能使combineReducers更加灵活(#1768、#1792 等) )。

自 Redux 创建以来已经过去了将近两年,我们现在对人们如何使用它有了一个很好的了解。 例如, @jimbolla在 #2214 中收集了所有已知商店增强器的列表,并对它们的工作方式和用途进行了分类。 我仍然非常喜欢 Redux 的简单性和灵活性,但我很想在 Redux 之上看到一些仍然惯用的抽象,它们将简化一般用法并为人们解决问题。

另一组半相关的主题,也是我个人对“封装逻辑/组件”(根据@slorber“带有 Elm/Redux 的可扩展前端”游乐场)和“插件-and-play Redux 设置”(如 https://github.com/brianneisler/duxtape/issues/1、https://github.com/jcoreio/redux-features/issues/7 等实验中所见)。 全局状态和应用程序设置对于某些用例非常有用,但对于其他用例则不然。

作为一个有趣的相关点, @toranbhttps://github.com/ember-redux/ember-redux上做了一些伟大的工作,为 Redux 创建了 Ember 包装器。 Ember 世界_真的_进入了“约定优于配置”,因此ember-redux设置了一个合理的默认商店配置,包括redux-thunk等。 某种类似的方法可能会有所帮助。

所以是的,我在这里抛出了一大堆不同的想法,但它们以各种方式相关。 总的来说,我想降低学习和使用 Redux 的障碍,并全面启用更高级的用例。

Redux 是一个通用的 API,而不是专门的。 这使得它更加冗长,同时也涵盖了更多的情况。 它的力量在于复杂的软件,而任何新的或简单的项目都超出了必要的范围。

但是,您可以在通用 API 之上构建专业化。 例如,我使用https://github.com/acdlite/redux-actions来减少常见情况的样板文件。 我仍然使用 redux 的全部功能,仅仅拥有该界面并不足以满足我的需求。

另一个问题是新人没有经历过极其复杂的应用程序的痛苦,他们想知道有什么意义。 好吧,对他们来说,在经历过那种痛苦之前,他们甚至可能不应该使用 redux,但是 Dan 的蛋头系列可以很容易地将他们踢过去。

https://egghead.io/courses/getting-started-with-redux
https://egghead.io/series/building-react-applications-with-idiomatic-redux

参考:抽象的频谱: https :

如前所述 - redux-actions非常擅长删除一些样板,通过附加.toString来删除等式中的常量是我喜欢的一个非常巧妙的主意,但是我更喜欢自己编写这个逻辑而不是使用redux-actions我自己,但这是保持我的代码更小的个人选择。

恕我直言,应该有一个内置机制( subscribe的回调参数?)来观察分派的动作流(我知道它可以在中间件中完成,但那是非常有限的用例,因为它不能使用外部,即通过组件或connect )

我经常将 redux 教给其他人,但由于以下原因,这可能非常令人生畏

  • 许多活动部件:存储、状态、reducer、动作创建者、动作、异步动作、中间件、连接的组件
  • 图书馆。 对于与 REST API 对话的简单应用程序,您需要:redux、react-redux、redux-thunk(或其他)+其他库(如果您想测试的话)
  • 您必须放置代码的位置数量(请参阅_移动部件_)

我认为 redux 是一个很棒的库,它本身就很有意义,我认为它具有很好的抽象性,并且在正确使用时具有超级可扩展性。 我也很欣赏 redux 可以与任何 UI 库一起使用,但我认为在大多数情况下它将与 react 一起使用。

一般来说,我相信很多挫折来自许多活动部件。

以下是我的一些想法。 所有这些都可以建立在 redux 之上,因此它可以保持原样。

  • 提供像 react redux 这样的库,它还支持内置的异步操作。
  • 许多异步操作都在与 REST API 通信,因此为此提供一个惯用的中间件
  • 将智能组件编写为实际组件,而不是使用连接。 然后他们可以在他们的render方法中渲染愚蠢的组件
  • 为 _container components_、_smart components_、_dumb components_、_action creators_ 和 _reducers_ 规定可扩展的文件布局。
  • 提供一个 _redux-cli_ 来生成代码,例如redux action loadUsers
  • 标准化像 redux-saga 或 redux-observable 这样的异步层,并将其包含在文档中(最好有 _one_ 地方可以咨询大多数 redux 问题)
  • 默认为扩展语言功能(例如装饰器(@connect))或 TypeScript 以减少冗长,特别是 mapStateToProps 和 mapDispatchToProps(mobx 需要装饰器,语法很简单)

同意, redux-actions是一些删除样板的简单抽象的一个很好的例子。

让我问一下:Create-React-App 有react-scripts包,它有预构建的 Babel 和 Webpack 配置,并带有一堆合理的内置默认值。 相应的redux-scripts包会是什么样子?

我同意其他一些关于redux-thunk (负面)评论。 我不会向初学者推荐它(我可能不再推荐它了):

  1. 开始似乎很神奇。
  2. 它并没有真正带走那么多样板。 给定一个更复杂的异步过程,必须从 props 中获取dispatch并将其传入工作只是代码的一小部分。
  3. 它导致您无法将动作创建者组合成更大的工作流程。
  4. 最后一点,我不能很好地表达它使 redux 风格更像是一个框架而不是一个库:你依靠框架来调用你的代码,而不是你调用库。

我们在 React-Native 应用程序中大量使用它,而且它非常有用,但是我们在将动作组合在一起时遇到了麻烦(想想“成功登录后加载配置文件”),因为 redux-thunk 动作创建者有效地返回了void (即没有任何东西,例如用于对下一个动作进行排序的承诺)。

它倾向于鼓励人们认为“做任何事情的唯一方法是立即从 React 事件处理程序/回调中调用dispatch ”。 因为我们使用connectreact-redux并且严重依赖mapDispatchToProps ,我们往往忘记我们的事件处理程序可能只是普通的函数。

(请注意,上面的应用程序大部分是在去年年初编写的,我们并没有真正跟上更大的 Redux 生态系统中发生的事情,所以它可能已经过时了)。

@bodhi :在 _too_ 没有太多切线的情况下,我不同意其中的一些结论。 redux-thunk有 11 行长,有一些很好的解释,比如“什么是 Thunk?” . Thunks _are_ 可组合到 _some_ 范围,我认为告诉某人“编写这几行函数声明,然后在此处进行 AJAX 调用等”通常更容易。 我在Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability 上有一篇博文讨论了 thunk(以及某种程度上的 sagas)的各种权衡。

我确实同意在某些假设的抽象层中需要有_some_“祝福”或“内置”方式来处理副作用。 Sagas 和 observables 都很棒,_如果_你知道如何使用它们。 我只是不知道它们是否合适,因为这里的潜在目标是提供一种轻松学习和使用 Redux 的途径。

我发现使用redux-thunk不仅仅是做一些异步的事情,而且还做一些依赖于当前状态的事情。 非常干净,简单的界面,并鼓励使用动作创建者。

Bodhi:也许有点跑题了,只是想也许值得一提的是dispatch不返回void,它返回的是你内部函数的结果。 因此,例如,如果您返回一个承诺,您可以轻松地将操作链接在一起。 请参阅自述文件中有关组合的部分:
https://github.com/gaearon/redux-thunk/blob/master/README.md

除此之外,我只想感谢社区进行这次公开讨论,听到每个人的想法和想法非常有趣。
到目前为止,我个人坚持 thunk,但试图将“发生了什么”与“状态应该如何改变”清楚地分开。 我在我们的代码中看到很多例子,我们犯了那个错误(使用动作告诉减速器做什么,而不是减速器只是对发生的事情做出反应),所以很高兴看到一些库以某种方式使这更难做(尚未在传奇上出售)。

2017 年 3 月 19 日 23:48,菩提通知@github.com 写道:

我同意关于 redux-thunk 的其他一些(负面)评论。 我不会向初学者推荐它(我可能不再推荐它了):

开始似乎很神奇。
它并没有真正带走那么多样板。 给定一个更复杂的异步过程,必须从 props 获取调度并将其传入工作只是代码的一小部分。
它导致您无法将动作创建者组合成更大的工作流程。
最后一点,我不能很好地表达它使 redux 风格更像是一个框架而不是一个库:你依靠框架来调用你的代码,而不是你调用库。
我们在 React-Native 应用程序中大量使用它,并且它非常有用,但是我们无法将动作组合在一起(想想“成功登录后加载配置文件”),因为 redux-thunk 动作创建者有效地返回了 void(即具有没有任何东西,例如用于对下一个操作进行排序的承诺)。

它倾向于鼓励人们认为“做任何事情的唯一方法是立即从 React 事件处理程序/回调中分派”。 由于我们使用 react-redux 中的 connect 并严重依赖 mapDispatchToProps,我们往往会忘记我们的事件处理程序可能只是普通的函数。

(请注意,上面的应用程序大部分是在去年年初编写的,我们并没有真正跟上更大的 Redux 生态系统中发生的事情,所以它可能已经过时了)。


您收到此消息是因为您订阅了此线程。
直接回复此邮件,在 GitHub 上查看,或将线程静音。

(很抱歉继续切线,但是......)

值得一提的是 dispatch 不返回 void,它返回内部函数的结果。

呵呵,太好了! 猜猜我只是在尝试找到另一种方法来解决我们的问题之前从未仔细查看过文档。

对我来说,让 Redux 变得如此容易的一件最有价值的事情是将操作视为事件而不是命令。 这完全消除了决定如何设计动作以及将什么放入其中的斗争。

组件不必知道用户的交互应该如何影响状态,他们只告诉外面的世界里面他们发生什么,如“按钮,点击X”。 将操作用作命令时,您可以有效地在组件和状态之间引入双向绑定。

Reducers 不绑定到组件,它们代表应用程序状态而不是组件状态,并且它们是唯一真正知道状态如何变化的一方。 在多个减速器中处理单个动作时没有认知障碍,因为动作不是“如何处理状态”的语句。

这也消除了同步分派多个动作的任何情况。

任何从多个地方分派单个动作的情况也会消失。 您可以将动作创建者放在组件附近,这样可以轻松追踪分派动作的组件。

通过查看 DevTools 中的操作日志,您可以查看应用程序内部发生的完整历史记录,并且很容易查看受操作影响的具体状态切片。 历史仍然是最短的。

这种基于拉取的方法使数据流真正单向并自然地与诸如redux-sagaredux-observable类的副作用管理库堆叠在一起。

@aikoven尽管我完全同意这一点(过去肯定有很多讨论将 redux 与事件溯源进行比较),但是您如何处理诸如FETCH_USER ,这很常见(以及一些需要完成)。 这看起来更像是“命令式”而不是“事件式”。

@blocka 总会有一些事件导致网络请求。 请求本身可以包含在FETCH_USER_STARTEDFETCH_USER_DONE / FETCH_USER_FAILED动作中,这就是请求结果在状态中的结束方式。

👍 redux-actions

除了减少样板文件之外,我还喜欢它的一点是,它感觉就像是 redux 的一个非常自然的扩展。 对于像jumpstate我觉得实现的距离有点远,现在我必须学习两件事而不是一件。

所以我的观点是,我们为减少样板文件而添加的任何插件都应该感觉非常像使用 redux,尽管它的 API 不那么冗长。

许多样板与您在任何环境中拥有的样板相同。 当您遇到重复时,我们通常会使用自定义抽象来解决它。 这些抽象通常在您当前的项目之外没有用,并且“银弹”是不可能存在的。 每个项目的要求都不一样。

与 redux 相同。 如果你认为你需要一个FETCH_X_STARTED动作,一个fetchX()动作创建者,还有 reducer 来处理它等等,你可以随意创建你自己的抽象!

为样板指责 redux 就像复制和粘贴所有代码,然后指责 javascript 有太多样板。

我发现使用 redux-thunk 不仅仅是做一些异步的事情,而是做一些依赖于当前状态的事情。 非常干净,简单的界面,并鼓励使用动作创建者。

将 redux-thunk 用于同步代码并不是一个好主意,应该在文档中提及。
如果您将许多功能组合在一起,您不确定调度流是否与顺序无关,因此更难推理和测试。

对我来说,拥有纯粹的动作创建者并简单地使用动作批处理来进行更复杂的状态修改似乎更好。 这样你就只能测试纯函数。

我推荐这篇博文以获得更好的理解: http :

@Machiaweliczny感谢您的文章。 我想我的问题更多是关于您最终将业务逻辑放在哪里(如https://medium.com/@jeffbski/where -do-i-put-my-business-logic-in-a- 中所述) react-redux-application-9253ef91ce1#.3ce3hk7y0)。 你不是说你的调度流(thunk、epic、saga 等等)永远不应该访问当前的应用程序状态,是吗?

@dfbaskin

请记住,Redux 不是领域驱动的开发。 “业务逻辑”在这里是一个非常模糊的问题,更多的是“什么概念的函数签名最适合我试图实现的逻辑”。 根据您的需要,您的“业务逻辑”分布在选择器、减速器、动作创建器和“编排器”(我们对此有更好的说法吗?)。

减速器(state, action) => state是“取决于状态的逻辑”通常应该在的地方。 果然,在像 Elm 架构这样的东西中,所有这些逻辑都在“更新”函数中(相当于我们的减速器)。

不幸的是,在 Redux 中,reducer 只能处理同步逻辑。 因此,依赖于状态的异步“业务逻辑”要么需要通过调度从组件中获取它(如果组件具有该信息,这并非总是如此),要么通过您选择的编排器概念(thunk、sagas、epic),哪个可以访问状态。

如果不是reducer的限制,就没有这个必要了。 当人们使用 redux-saga 或 redux-observable 时,您实际上会看到它:他们的动作/动作创建者通常变得近乎平凡(在大多数情况下,加上或减去一些规范化或格式),而传奇/史诗几乎是“替代减速器” , 在那之后,您现在有了另一个可以访问操作和状态并返回“事物”的东西,因此实际减速器的复杂性降低了。 Reducers 和 Orchestrator 的关系非常密切(有时太密切了,并且有 2 个构造在一半时间可以互换并不是很好……但这就是我们所拥有的,不妨享受它)

@Machiaweliczny :作为“

更好的讨论,但我们开始有点偏离主题了。 (那是我在那边看到的自行车棚吗?:))

让我回到最初的问题:

  • 带有“较少样板”的惯用 Redux 用法会是什么样的? 我们如何为这些投诉提供解决方案?
  • 可以创建哪些可能的抽象来简化学习和使用过程,但又不会真正隐藏 Redux(并希望提供一个迁移/学习路径到“基础”Redux)?
  • 有多少可以通过改进文档以某种方式解决?

我们可以采取哪些具体步骤来解决这些问题?

在无可争议的、可操作的、短期步骤的领域,我会说:

1)让商店更容易配置(也许是默认配置?我不知道现在是否仍然如此,但是Elm曾经有一个“starter app”类型,和一个普通app类型。使用的是starter app对于简单的应用程序和培训材料,以涵盖人们刚刚学习时的 90% 情况。直到今天,我承认我无法在不查找签名文档的情况下使用基本中间件(devtools、thunk)配置 Redux 商店。我不认为很多人会争辩说,新手需要知道如何配置商店的中间件才能学习 Redux 核心概念。

2) 可能已经是这种情况,请确保将带有上述“starter store”的 Redux 添加到 create-react-app 中尽可能简单,这样新手可以在 2 分钟内启动并运行 Redux 应用程序。

我认为这会让我们走得很远很远。

编辑:通过入门应用程序,我不是指应用程序样板,而是指无法配置但具有合理默认值的“createStore”,人们可以导入并使用它。 然后他们可以在长大后继续使用“真正的”createStore。

这听起来是一个非常棒的概念。

2017 年 3 月 20 日星期一上午 10:02 Francois Ward [email protected]
写道:

在无可争议的、可操作的、短期步骤的领域,我会说:

1.

使商店更容易配置(也许有一个默认的
配置? 我不知道现在是否仍然如此,但 Elm 曾经有过
“入门应用”类型和常规应用类型。 入门应用程序用于
简单的应用程序和培训材料可以涵盖 90% 的情况
只是学习。 直到今天,我承认我无法配置 Redux 商店
使用基本的中间件(devtools、thunks)而无需查找文档
签名。 我不认为很多人会争辩说新人需要
了解如何配置商店的中间件来学习 Redux 核心
概念。
2.

可能已经是这种情况,请确保使用上述内容添加 Redux
create-react-app 的“starter store”尽可能简单,所以新手
可以在 2 分钟内启动并运行 Redux 应用程序。

我认为这会让我们走得很远很远。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/reactjs/redux/issues/2295#issuecomment-287806663或静音
线程
https://github.com/notifications/unsubscribe-auth/AFUmCQYkUwjkCugevDBWC8TOu0HhWJTnks5rnqMWgaJpZM4MhnVF
.

我们的模块使用combineReducers以便模块中的每个状态切片都有一个专用的减速器(我相信这种方法在文档的结构减速器部分中概述,又名“切片减速器”)。 这使事情更容易推理,因为 switch 语句要小得多。 它还允许在我们的代码库中出现常见的 reducer。 除了它们的动作类型外,最简单的reducer在模块之间是相同的,所以我们能够通过辅助函数(reducer factory?)来减少样板文件,通过动作类型为我们制作这些reducer:

const { makeIndicator, makePayloadAssignor } from '../shared/reducers';

const searchModule = combineReducers({
  searching: makeIndicator(c.SEARCH),
  results: makePayloadAssignor(c.SEARCH_RESPONSE, [])
});

也许像这样识别更常见的切片减速器将是减轻样板问题的好方法,它们可以作为我们减速器的原始构建块。

这是基本的 javascript 重构。 正如我之前所说......“javascript”有一个
很多“样板”,直到您将抽象之类的东西应用到您的代码中。

Redux 只是 javascript。 它没有什么神奇之处。

2017 年 3 月 20 日星期一下午 12:44,Markus Coetzee通知@ github.com
写道:

我们的模块利用 combineReducers 使每个状态切片
模块有一个专用的减速器(我相信这种方法已概述
在文档的结构化减速器部分中,又名“切片
减速器”)。这使事情更容易推理,因为开关
声明要小得多。 它还允许常见的减速器出现
在我们的代码库中。 最简单的 reducer 跨模块是相同的
除了他们的动作类型,所以我们能够减少样板
具有帮助函数(减速器工厂?),使这些减速器用于
我们按行动类型:

const { makeIndicator, makePayloadAssignor } 来自'../shared/reducers';
const searchModule = combineReducers({
搜索:makeIndicator(c.SEARCH),
结果:makePayloadAssignor(c.SEARCH_RESPONSE, [])
});

也许像这样识别更常见的切片减速器会很好
减轻样板问题的方法,它们可以作为
我们的减速器的原始构建块。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/reactjs/redux/issues/2295#issuecomment-287820595或静音
线程
https://github.com/notifications/unsubscribe-auth/AACourLpL5--NJiRzrWmKFJ31cl5DXJrks5rnqzxgaJpZM4MhnVF
.

我同意大多数评论者的看法,即解释常见的 redux 模式比开发一些样板化的 reducer 更好。 我和我的队友在 1.5 年前开始使用 redux 并且对最简单的事情感到困惑:redux 主要是事件溯源,同一动作可以由多个减速器处理,该动作可以包含整个业务实体,而不仅仅是它的 id ,reducer 只是函数,你可以自由地用它们做任何你想做的事情——组合、生成、拆分等。我们犯了常见的错误——将动作用作远程命令,并想知道为什么不使用经典的方法调用; 创建了巨大的中间件并避免使用 redux-saga,因为“生成器太复杂了”(尽管 redux-saga 很棒!),使用巨大的开关制作长文件。 这当然是因为编程技能平庸,但也因为缺乏结构化信息。 今天的文档好多了,非常感谢维护者!

这是 Redux 作为库的一个非常有价值的话题。

这是基本的 javascript 重构。 正如我之前所说......“javascript”有一个
很多“样板”,直到您将抽象之类的东西应用到您的代码中。
Redux 只是 javascript。 它没有什么神奇之处。

完美,@blocka! 我认为这个信息需要传播。

Redux 因其最小的方法而让人们感到不寻常和样板化。 对于一般的“框架”(最少的代码,最少的原则),Minimal 感觉很奇怪。

有些人习惯于编写“框架式”代码而不是编写 JavaScript 本身。 当开发人员使用一个框架时,内置的方式来做事情是隐含的预期。 悲伤如地狱,但真实。

我相信 Redux 比以往任何时候都更容易上手,但我们仍然需要让人们清楚不要期望 Redux 做所有事情。 @gaearon 一直清楚地说明这一点,但不幸的是,人们已经习惯了框架做任何事情。 为什么他们应该有动力去学习一些“没什么用”的东西? Angular 比 Redux 做得更多。 为什么是 Redux? 在这个问题上,对于我们所有人来说,答案可能是显而易见的。 但是对于初学者来说清楚吗?

我昨天写了一篇关于它的简短文章:不要把它归咎于 React 或 Redux 。 当然,我可能在这一切上都错了。

在高级库之上有抽象层是有先例的。 就在最近,Tensorflow 引入了 Keras 作为核心 API 之上的抽象: http :

使用 TensorFlow 让我觉得自己不够聪明,无法使用 TensorFlow; 而使用 Keras 让我觉得神经网络比我想象的要容易。 这是因为 TensorFlow 的 API 冗长且令人困惑,而且 Keras 拥有我所体验过的设计最周到、表达能力最强的 API。 在与 TensorFlow 的前几次令人沮丧的互动之后,我太尴尬了,不敢公开批评 TensorFlow。 感觉如此笨重和不自然,但这肯定是我的失败。

我觉得对于大多数初次接触 Redux 的人来说,这是一种类似的体验。 将Tensorflow替换Redux并将Keras替换Jumpstate 。 Redux 非常强大,但大多数用户可能并不需要所有可用的控件。 更有可能的是,他们来自 Angular,或者在训练营中学习 React + Redux,或者观看了各种教程。 虽然 Redux 不需要为新用户简化,但它也不是提供更简单抽象的反模式,可能涵盖 80% 的潜在用例。

你们大家,如果 redux 就这样就好了,我们就不会进行这种对话。 无论 redux 是否有用或结构合理,人们在第一次学习它时仍然会感到“糟糕”。 这些感觉是有道理的。

在这个帖子里评论的人都是 redux 专家——当然我们对 redux 很满意; 我们已经内化了它的理念,我们可以轻松地为管理“样板”创建我们自己的抽象。 我们不是该线程打算服务的受众。 我们需要对第一次接触 redux,甚至可能是第一次接触函数式编程的用户产生一些共鸣

例如:这是我经常听到的关于 redux 的抱怨(我认为在线程上没有提到过):仅使用 redux 是不够的,您还“需要”使用 react-redux、redux-thunk、redux-actions 等.

你真的需要使用所有其他包来使用 redux 吗? 不。
大多数 redux 用户最终会使用部分或全部这些附加包吗? 是的。

package.json的包裹数量重要吗? 不,不是真的。
package.json的包裹数量会影响人们的感受吗? 绝对地。

现在,我认为相信 redux 本身应该保持原样是公平的,另一个包( create-redux-app或其他)应该将它们组装在一起。 但是我们手头有一个真正的复杂性问题,它不足以告诉用户 RTFM。

无论 redux 是否有用或结构合理,人们在第一次学习它时仍然会感到“糟糕”。 这些感觉是有效的

绝对地。 但并非所有人都有这种感觉。 有些人会。 请记住,您永远不会收到没有问题的人的来信。 你只能看到问你问题的人,或者点击 stackoverflow/reactiflux。 在那些从不这样做的人中,有些人也需要帮助但不知道如何寻求帮助……有些人也做得很好,您不想让他们的情况变得更糟……否则他们会成为来论坛的人,这将是净零收益。

Redux 没有无缘无故地流行起来。 很多人认为它感觉非常好并推荐给其他人:)

现在,我认为相信 redux 本身应该保持原样是公平的,另一个包(create-redux-app 或其他)应该处理将它们组装在一起。

我喜欢这个主意。 可能会争辩说现有的样板已经解决了这个问题,但当时 React 也有很多样板,人们抱怨在尝试进入 React 时被工具淹没。 create-react-app是对这一切的一个很好的回答,因为它已经为初学者消除了很多步骤来进入 React _itself_。

但它仍然是引擎盖下的样板。 因此,我们仍然需要鼓励人们购买生态系统,并明确表示 Redux 本身在设计上并没有做太多事情。 如果你想使用 React 或 Redux,就不可能脱离生态系统。

我不相信在 Redux 核心中包含诸如redux-thunk包是实现这一目标的方法。

只是为了清楚起见,我抛出的那些想法实际上并不是将事物直接包含在核心中。 他们假设创建某种“预先构建的配置”或“易于使用的抽象层”,这将是一个单独的包或 CLI 工具或其他东西,更多的是沿着create-react-app的路线。

我想我最好加入 Jumpstate/Jumpsuit 的作者。

特尔;博士:
Redux 很难学习,也很难配置/集成,因为它太低级了。 社区可以从针对 90% 用例的标准化更高级别 redux api 中受益,以简化学习曲线,甚至可以作为更简单项目的解决方案运行。 Jumpstate 正试图解决这个问题,但不是一个长期的解决方案。 我们需要开始就这个 api 的外观提出可能的想法(真实或元代码)。


Redux 一开始就很难学习,而且通常也很难与异步逻辑、副作用、自定义中间件等新概念集成。一年多前,我自己也经历过这一点,当时我对 React 生态系统来说是非常新的。

在学习 react 的几天后,我立即被推荐学习 redux。 这个建议来自于曾经(或正在做)同样事情的非常有经验和新手的开发者。 它被誉为超越 setState 的任何事情的事实上的解决方案(尽管 😉 让我们将 setState 门排除在外)。 所有这一切都是有充分理由的,因为 redux 很棒。

我不可避免地遇到了学习曲线,当寻求帮助时立即意识到 redux 的营销和记录方法呈现出“Redux 很难,概念是低级的,api 被锁定,你只需要通过它。”

所以这就是我和我所有的同龄人一起做的。 我们通过它提供动力,因为这是我们受过的教育。 尽管如此,在这个过程中,我很早就敢于创建一个抽象层,它会暂时有意地隐藏一些 redux 必须提供的复杂性和高级功能。

我不需要自定义动作创建器,不想担心将动作和调度程序映射到组件,并且像许多其他人一样,选择了一个不太冗长的 reducer 布局来代替 switch 语句。

它工作得非常好。 太好了,事实上,它加快了我其他同龄人和其他同样在学习曲线上苦苦挣扎的开发人员的学习 redux 的过程。

我们以这样一种方式设计它,即在我们需要时仍然可以使用高级或“惯用的”redux,但真正为我们 90% 的用例设计它。

我绝不是说 jumpstate 已经涵盖了所有基础,事实上,还有很多事情需要改变才能让 jumpstate 表现得“惯用”(可定制的动作创建者,删除全局动作导入......有很多) ,但它确实帮助了很多人学习了基础知识,并为他们提供了一条在非常简单的层面上理解 redux 如何工作的途径。 我一直在 Jumpstate slack 组织中收到消息,说 jumpstate 帮助某人学习了 redux。

这些人今天是否仍在使用 jumpstate 并没有那么重要(但许多人今天仍在使用它生产)。 重要的是他们拥有的经验,以及他们能够多快在 redux 生态系统中变得高效。

本次讨论中想到的更多想法:

  • 提出简化的 redux 外观的元示例,只需尝试一下!
  • 讨论经常重复和极端自以为是的 redux 中间战的要求并决定标准。 我们可以从异步处理开始。 这可以解决 redux 社区中如此多的碎片化问题,并帮助每个人的努力对 redux 的长期生存和使用产生影响。

有多少可以通过改进文档以某种方式解决?

很多。 谈到 Redux 文档,我觉得它在很多方面都比 AngularJS 1 的文档差,因为与 AngularJS 的文档一样,它不必要地冗长,但与 AngularJS 的文档不同,它不符合“显示,不说”原则。

Redux 的文档更喜欢代码片段而不是代码的实际演示,这意味着“这里有一组代码;我们不会告诉你它是如何连接的,但相信我们它可以工作”的时刻太多了。 缺乏在浏览器中运行示例的能力意味着用户被迫相信自己的直觉,即他们在本地所做的事情是有效的,如果某些事情不起作用,他们就会自行离开。 即使是简单的 Todo List 应用程序,我也觉得它不是 Redux 最好的“hello world”示例——它可以变得更简单。

图表在这里肯定会有所帮助。 例如,我喜欢@sunjay的一个; 一种可能更好的方法是为 Todo List 示例本身设计一个 - 从高级角度来看文件是如何连接的等等。 图像几乎总是更适合传达长文本,并且可以帮助减少文档的冗长。 这些图像还可以帮助初学者保持注意力并更好地理解整个 Redux。

我的两分钱:请不要改变核心。 它的辉煌在于它的简单。 在这一点上,添加一些东西可能会影响整体。

用户友好性和进一步的抽象可以发生在用户空间中。 我也不觉得需要有任何官方或惯用的方式来使用它。 这是一个可预测的状态容器,就是这样!

它可以自己站立,让它成为❤️

也就是说,当然,通过在它之上创建更多有意见的库来打包它。 这也是我所做的,但我们不要改变核心,拜托了。

@HenrikJoreteg ,不要害怕! 我几乎可以肯定,我们在这里讨论的任何内容都不会改变核心。 核心功能齐全,独立存在。 :)

很多。 谈到 Redux 文档,我觉得它在很多方面都比 AngularJS 1 的文档差,因为与 AngularJS 的文档一样,它不必要地冗长,但与 AngularJS 的文档不同,它不符合“显示,不说”原则。

这是非常真实的。 Redux 刚出现时的一件事是文档很少,但基础知识就在那里。 许多人采用它是因为它“如此简单”。 但是现在在线资源试图展示“现实世界的技术”,这些技术通常只是“我最喜欢的中间件”和“我最喜欢的样板缩减工具”。 人们不知所措。

关于“少即是多”有话要说

@Phoenixmatrix@Sylphony :那么,潜在的文档改造会是什么样子?

就我个人而言,要将它与我上面关于拥有“原型商店”的建议结合起来,我会将它分成两部分(不是 2 个不同的站点/文档,而是 2 个不同的部分)。

“Redux 架构”有很多可爱、简单的绘图,解释了 action 和 reducer,介绍了 react-redux 和 thunk,使用“starter store”来限制任何类型的前期配置,并展示了它是多么容易制作一个简单的应用程序,它会执行一些 ajax 请求并在屏幕上呈现内容。

其他所有内容都可以放在具有更高级概念的“探索”部分。 即便如此,我也可能会撕掉很多东西。 今天有人向我指出,有一个部分是关于在一个应用程序中拥有多个商店。 知道这点很好,但它的用途真的很先进。 我认为任何新人都不需要不看就偶然发现它。

更好的文档总是一个好主意,但我认为这并不能解决这里的核心问题。 由于 Redux api 的样板和冗长,正在编写和采用抽象库。 那些相同的库正在分裂 Redux 社区,因为它们显然在某种程度上是需要的。 当那些积极尝试为生态系统做出贡献的作者因为“不了解 Redux”而被公开解雇和解雇时,令人沮丧。 如果社区可以创建一个更简单的抽象,而不是 React 核心的一部分,而是在某种程度上“祝福”,那么每个人都会赢。 铁杆用户继续按原样使用 Redux,其他人可以使用具有更简单/更简洁 api 的抽象。

@derekperkins我们如何就“受祝福的”图书馆达成共识? 我们如何决定其中的内容以及由谁来处理?

我完全赞成这样的事情,但是我们如何避免仅仅创建另一个只有某些人使用的库,从而进一步分裂生态系统。

相关xkcd: https ://xkcd.com/927/

是的,redux 的核心和 react-redux 尽可能接近共识。 甚至 redux-thunk 和 redux-actions 也相当边缘化,因为即使在那个级别,大多数人也有不同的需求。

减少 Redux 中的样板是“我不需要所以我会把它抽象出来。 但即使是新人,“那部分”也有很大差异。

哎呀,我们有很多(几十个)redux 应用程序在工作,并且很难构建一个通用的样板减少堆栈,因为不同的应用程序有不同的要求。 当您拥有一个应用程序或几个具有相同要求的应用程序时,这很容易。 即使是小的也有很大的不同。

这里的想法不是将一个工具包放在一起,为所有人解决所有问题。 以 CRA 为例:有很多方法可以配置 Babel、Webpack 和 ESLint。 CRA 所做的是提供一种单一的、固执己见的方式,让它们都能正常工作,而不会对用户造成任何麻烦,并且它特意挑选了一些设置,使学习者的学习变得更好。 然后它允许用户控制配置_如果他们愿意_。

它也不会尝试解决每个用例。 CRA 不会尝试解决高级代码拆分、多入口点、SSR 或类似问题。 CRA 文档 _do_ 指向其他工具和选项,这些工具和选项可能提供更多可配置性并在某些用例中更好地工作。

这是一个很好的表达方式。 一些可以快速启动和运行的东西
只涉及安装 redux 和这个假设的配置层? 那
会很棒。

我的另一个想法是你可以在开发模式下设置一个标志,它可以
注意并为用户提供增量学习机会。 在
通过这种方式,抽象层可以作为交互式教学工具。
2017 年 3 月 20 日星期一下午 4:14 Mark Erikson通知@ github.com
写道:

这里的想法不是将解决所有问题的工具包放在一起
对于所有人。 以 CRA 为例:有很多方法可以配置
Babel 和 Webpack 和 ESLint。 CRA 所做的是提供单一的、固执的
使它们全部正常工作的方法,而用户方面没有任何麻烦,并且
特意挑选了一些使事情变得更好的设置
学习者。 然后它允许用户控制配置,如果他们想要

它也不会尝试解决每个用例。 CRA 不会尝试
解决高级代码拆分、多入口点、SSR 或类似问题
那。 CRA 文档确实指出了可能提供的其他工具和选项
更多的可配置性,并在某些用例中更好地工作。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/reactjs/redux/issues/2295#issuecomment-287914805或静音
线程
https://github.com/notifications/unsubscribe-auth/AFUmCactFzsw7etmGGP8MFK6kfNaOzTJks5rnvorgaJpZM4MhnVF
.

@sunjay我什至不用点击就知道是什么 xkcd 链接。 :)

我同意@markerikson。 这并不是试图为每个人处理每一个边缘情况。 这是来自 Redux 核心开发人员的固执己见的说法,“这是让事情正常工作的好方法”。 如果需要,可以覆盖合理的默认值,但我敢打赌默认值将适用于 80% 的开发人员。

这可能会或可能不会写在核心 Redux 上。 我仍然认为 Jumpstate 之类的东西可以做的不仅仅是加速学习曲线。

Redux 学习曲线可能很陡峭,但是一旦掌握了概念,对抽象层的需求通常就会消失

我认为这在一定程度上是正确的,但并不一定意味着您总是要消除抽象层。 尽管我过去写过 C 并且了解内存分配,但我更喜欢用 Go 编写,在那里为我处理垃圾收集。 根据我之前的 Tensorflow 示例,写入较低级别的 apis 的性能会更高,但对于 Python keras 抽象的易用性也有很多话要说。 类似地,对于 Redux 与 Jumpstate(或来自此问题的任何抽象),并不是说样板不一定难写,只是对于不需要原始 Redux 的所有灵活性的大多数用例来说很麻烦。

如果由我决定,我想看看这个:

  • 一个 Flow/TypeScript 友好的子集,比普通 Redux 更容易输入。
  • 不太强调常量(只是使使用字符串文字更安全)。
  • 保持独立于 React 或其他视图库,但更容易使用现有绑定(例如提供mapStateToProps实现)。
  • 保留 Redux 原则(可序列化的动作、时间旅行和热重载应该有效,动作日志应该有意义)。
  • 以更直接的方式支持代码拆分。
  • 鼓励将 reducer 与选择器并置在一起,并减少它们一起编写的尴尬(想想易于编写和组合的 reducer-selector 包)。
  • 而不是将动作创建器与减速器并置,完全摆脱动作创建器,并使动作到减速器的多对多映射变得自然(而不是像大多数图书馆那样回避它)。
  • 设置合理的性能默认值,因此通过 Reselect 进行记忆“仅适用于”常见用例,而无需用户编写该代码。
  • 包含用于索引、规范化、集合和乐观更新的内置帮助程序。
  • 具有内置的可测试异步流支持。

当然,在核心之上,尽管将其标记为正式的 Redux 东西也不错。
另外,我不会写这个。 但是你可以。

对我来说,几个月前,当我学习redux ,我阅读了文档但不太了解,所以我花了 2 天时间阅读了src 代码,然后每一个关于api的描述文档变得清晰

然后,关于“状态设计”、“reducer 划分”、“中间件设计”、“异步解决方案”......章节的想法随着我在下个月的项目开发中变得清晰。

所以,我认为阅读 src 代码是学习redux合适的一步,只有这样,你才能更顺畅地理解和探索redux的环境。

默认支持异步。

我的意思是不需要使用另一个插件,例如redux-thunk, redux-saga

默认支持异步

确实如此,只是人们在寻找更好的东西之前会看它 0.5 秒。 没有什么能阻止您在组件内调用 API,然后在回调/承诺解决方案中调用 dispatch。 只是人们想立即将其具体化(包括我在内)。 但是你可以在没有任何中间件的情况下很好地进行异步。

@gaearon的评论是完美的,因为它突出了扫描范围的广泛性。 我非常喜欢并欣赏他提出的 Redux 2.0 的精确、优雅和强大,但这些改进几乎与我无关!

我真的觉得这很迷人。

我自己的 Redux 抽象几乎完全相反。 我发现单个reducer 操作、将原子reducer 函数与其操作代码和选择器捆绑在一起、完全杀死字符串以支持对象键的巨大价值。 这是我自己的 90% 解决方案,在态度上更类似于 CRA。

有趣的一点是:我们试图解决什么问题? 有很多问题,有几个不同的解决方案空间可用。 如果 Dan 按照他描述的方式构建 Redux 2.0,我也会在此基础上重建我自己的抽象库!

@jbellsey Redux 在为应用程序全局提供操作和状态方面非常出色——但在定义的范围内——你可以拥有无​​限的 redux 存储实例,可以将它们组合起来。 基本上这对图书馆很有用。 如果你看到一个库在里面谈论 redux,你可以从......

我们试图解决什么问题?

应用程序的全局状态[R eliable A E sthetics d eliveredünified E X tensible:心脏:

没有人想要全局变量。 但每个人都需要一个 :tm:

请像现在一样保持 redux 的简单。 是图案。 食谱总是在 :100 左右出现:

@markerikson启动文档/电子表格来组织这些想法是个好主意吗? 已经有一些很棒的想法了,而且只有 48 小时 🎉

对我来说,我不建议人们使用 thunk 或 saga。 相反,学习 JavaScript 中的依赖注入技术。

一旦我把它当作一个普通的数据库,Redux 就会变得更容易:

  • 我告诉 Redux 初始状态,以及一个动作(事件)如何影响应用程序的状态。 (如数据库架构和存储过程。)
  • 然后 Redux 负责管理它并通知侦听器,并提供检查和调试状态的方法。

对我来说,这就是 Redux 的全部(我不希望数据库进行 HTTP 调用)。 对于 I/O 和异步内容,我在 Redux 之外执行。 这使我的 Redux 存储保持可预测性。 类型签名始终store.dispatch(actionObject) 。 没有调度函数,动作内部也没有隐藏的含义(例如,调度动作不会进行 API 调用)。

@tannerlinsley :是的,去吧。

一个缺点我开始这个讨论:我已经有了一的东西,我需要工作和写。 其中大部分是我自己强加的,但这意味着我没有足够的时间来研究一些我想研究的与 Redux 相关的主题,更不用说解决诸如主要文档修改或构建新工具包之类的事情了划痕。

我绝对想参与这些可能的努力,但这需要其他人加入并共同努力。 (每个 OSS 维护者的著名遗言...... :))

一旦我把它当作一个普通的数据库,Redux 就会变得更容易

这是我推荐的!

  • 保持您的商店正常化
  • 你可以准备数据——比如视图。 通过专用的减速器执行此操作 - 然后您不需要reselect 。 考虑。
  • 动作不应触发其他动作——这看起来很容易。 但是如果你有componentDidMount钩子就很难了。 我不知道如何解决这个问题。 在层次结构中向上移动并不总是那么容易

FWIW,我一直在使用 Redux 和D3.js (没有 React),并且进展顺利。 d3-component中列出了一些示例。

我最怀念 React Redux 绑定的地方是connect 。 从@gaearon的上述大纲/愿景中,我最感兴趣的mapStateToProps实现”。

你可以准备数据——比如视图。 通过专用的减速器执行此操作 - 然后您不需要重新选择。 考虑。

是的,这就是 twitter 在其商店中所做的,至少我是这么认为的 - 他们有entities分支,其中包含数据映射的常规 ID,即timeline视图,这基本上是您的主要供稿查看哪个包含对实体分支的 id 引用,但它已经预先计算好了。

这就是我在即将进行的重构中要做的事情,因为重新选择高度动态的列表......不是太高效;) 我看到的唯一缺点是你最终会得到重复的数据,并且还可能会操纵视图的更新和删除从长远来看会很麻烦,因为你需要经常记住大约 2 个地方(或更多)。

@stefensuhat当我尝试自己实现 Redux 时,我使用了 Promise 循环来使异步操作成为可能。 您可以在https://github.com/iddan/delux 上查看

@gaearon完全同意你的清单! 我们一直在按照我工作的组织中 90% 的列表(也遵循鸭子原则)开发 redux 样板。 目前这是一个早期版本,尚未准备好发布。

我绝对愿意为这个好主意做出贡献!

@markerikson @tannerlinsley

至于组织这些想法,我建议您在制作电子表格后在适当的存储库中创建 Github 问题以跟踪开发。 然后,您应该在此处评论这些问题的列表。

这并不能保证任何特定的东西都会随 redux 一起发布,但它确实给了对特定事物感兴趣的人一个机会来关注讨论并共同努力实现它。

将这些建议变为现实的重担是我们所有对每个新功能或方法感兴趣的人。 尽管马克开始了讨论,但我相信没有人期望他去实施这一切。 :)

是的, @gaearon的列表是理想的,但正如一些人已经提到的,这是很多东西。 任何试图直接解决它的人都将面临自行车棚被遗忘的危险。 无论如何,我都希望看到它发生,但我认为应该一次攻击较小的部分,而不是在顶部构建一个完整的层(再次推动我的预配置原型商店示例;))

话虽如此,当/如果有人确实试图攻击整个列表,还要记住在 Redux 之外,甚至在 React 之外还存在哪些其他工具和框架。 React/Redux 比 vanilla Flux、MobX、VueJS 及其生态系统、Angular 有一系列有趣的好处。 无论社区做什么,它可能希望继续关注其中的一些好处(其中一些是@gaearon列表的一部分),并确保构建在顶部的任何层都不会变得完全多余。 时间旅行调试、中间件生态系统等很多东西都可以在其他地方获得。

我正在编写的示例:制作一个模块可能很诱人,让您制作一个自动更新自动生成的减速器的函数。 但是一旦你这样做了,你就复制了 MobX,所以这不是很有用。

@Phoenixmatrix是的,我的意思是 redux 具有默认中间件,因此我们不需要使用外部中间件。 😄

我真的没有看到 Redux 当前实现有什么问题。 Boilerplate 存在,但如果你有一个类型系统,这真的没什么大不了的。

对我来说一个主要问题是人们似乎不明白我们正在构建比几年前更复杂的前端应用程序。 如果我们在大型团队中构建更大、更复杂的应用程序,对我来说难度显着增加,新模式出现,但人们拒绝这种困难是正常的。

我并不是说我们不应该试图让事情变得更简单,或者减少样板,但人们不应该开始使用 Redux 并在不了解所进行的权衡和看到大局的情况下抱怨所涉及的样板。

创建 Redux 时,很多人(包括我在内)都在尝试构建那个东西, @gaearon得到了最好的实现,并且在工具/演示方面做得非常好。 它(还有 Flux)最初受到了很大的关注,不仅是因为时间旅行,还因为这个概念对很多人来说并不新鲜,而且立即感觉很自然。

人们将 Redux 视为一种新的闪亮玩具(这在实践中似乎并不那么容易)。 然而 Redux 中没有什么是真正的新东西,除了有人确实以一种优雅的方式将这些概念移植到前端。 Redux 的模式及其生态系统已经在分布式系统、事件源后端应用程序、领域驱动设计中使用,并且有这些背景的人会很容易理解 Redux。

不幸的是,人们想要跳过步骤并使用 Redux,而不了解所涉及的更一般的模式。 我个人建议任何想要在 Redux 中构建一些严肃的东西的人都具有最少的分布式系统背景。 这可能看起来很奇怪,但我认为拥有这种背景可以在现在和未来几年为您带来非常重要的优势。

当我们构建前端应用程序时,我们究竟在构建什么?

  • 我们构建一个分布式系统的成员
  • 该成员只持有更一般系统的数据的一个子集
  • 该成员的容量非常有限(存储、带宽、网络稳定性...)

因此,我们尝试构建非常复杂的东西。 我们有比数据库开发人员更难处理的约束,但我们希望它更简单,开箱即用,无需重复使用那些多年来真正认真思考过这些问题的人的先前知识。 你不认为人们已经考虑过如何在 DB 内部构建状态吗? 如何查询呢?

我并不是说我们今天不应该务实,不应该用我们目前的知识来发布应用程序并犯错误,而是要理解并承认我们正在构建的东西不再简单,需要额外的知识。 Redux 可能是获取这些知识的好途径,一开始你可以以务实的方式使用它,但不要低估未来几年前端开发会让你感到不舒服的程度。

不幸的是,人们想要跳过步骤并使用 Redux,而不了解所涉及的更一般的模式。

无论好坏,Redux 基本上赢得了 React 状态管理之战。 随之而来的是塑造生态系统的能力。 总会有一些开发人员只想要一些有用的东西,并且有很多更简单的应用程序是必要的。 人们要么继续使用其他抽象层,要么可以通过一个简单的库将他们引入正确的通用模式。

我个人建议任何想要在 Redux 中构建一些严肃的东西的人都具有最少的分布式系统背景。 这可能看起来很奇怪,但我认为拥有这种背景可以在现在和未来几年为您带来非常重要的优势。

这是问题的关键。 Redux as is 对于理解它的人来说非常有用。 然而,简化它不一定是坏事。 大多数人不再编写汇编代码,即使理解它通常可以帮助您编写更好的代码。 这不是大多数人开始的地方。

周围还有很多好的评论。 这里有很多想法,我们可以而且应该进一步探索。

让我们看看我们是否可以开始提出一些可能可行的想法。 我将首先抛出一个小的基线概念:

创建一个名为,我不知道, redux-starter或其他名称的新包。 该包可能依赖于redux-actionsredux-thunkredux-logger ,也可能依赖于一个基于 Promise 的中间件。 它将提供一个带有function createReduxStore({reducers, middleware}) {}签名的函数(基于@Phoenixmatrix之前的评论)。 甚至可能不愿意接受中间件?

createReduxStore()函数将负责处理applyMiddleware并为 root reducer 设置热重载,以及配置 Redux DevTools 扩展。 如果没有提供中间件,它会默认配置包含的中间件。 这可能类似于ember-redux的商店服务设置代码我的“Project Mini-Mek”示例应用程序中

这至少可以最大限度地减少启动所需的设置量,并提供一组适度的有用功能。

是的,我喜欢那个(嗯,不喜欢新用户的 redux-actions 和基于 promise 的中间件,但我会让你在那个自行车棚上玩得开心)。 不会打扰接受中间件,只需 createStarterStore(reducer)。

甚至可以替换 combineReducer,因此您可以简单地执行以下操作:

const store = createStarterStore({
   foo,
   bar,
   baz
});

其中 foo bar baz 是减速器。

德拉特。 刚刚意识到“为 root reducer 设置 HMR 的库函数”可能不起作用,因为即使我们允许用户传入他们的 root reducer 文件的路径, module.hot.accept()一个或两个require(path)重新导入步骤需要在构建时可以静态分析而不是变量的路径。 (我想……我可能是错的。)哦,好吧。

但是,是的, createStarterStore()可以接受 root reducer 或 slices 对象。

我个人建议任何想要在 Redux 中构建一些严肃的东西的人都具有最少的分布式系统背景。 这可能看起来很奇怪,但我认为拥有这种背景可以在现在和未来几年为您带来非常重要的优势。

@slorber我同意你的观点。

不幸的是,人们想要跳过步骤并使用 Redux,而不了解所涉及的更一般的模式。

但不是真的在这一点上。 有些人确实知道 Redux 中涉及的模式,这正是他们选择使用它的原因。 想要使用简单(或至少更简单)的东西并不一定意味着用户不能使用更困难的实现。 它只是节省时间和精力。

在我工作的地方,我们已经使用 Redux 一年了,发现自己复制/粘贴相同的代码(只是稍微修改了一下)来向商店添加新资源,这就是为什么在某些时候我们觉得需要在 Redux 之上实现一个层,以抽象这个冗余样板。
在我看来,这个想法不仅简化了 Redux 的使用,而且还减少了最常见部分的样板(以同样的方式react-redux在将 Redux 与 React 一起使用时减少了样板)。

非常有趣的讨论。 我从一开始就作为 redux 用户的两分钱:

一开始,我记得,redux 被更多地视为一种协议或一组合理的约定,而不是构建应用程序的框架。 这种紧缩是为什么 redux 可能会让新人不知所措并且在不滚动您自己的抽象的情况下扩展起来很乏味的部分原因。 但它在 2015 年摆脱管理状态混乱的简单方法也是它起飞的一个原因,极简主义允许像 Apollo 这样令人惊叹的框架搭载为不同项目(尤其是开发工具)完成的工作,并提供一种熟悉的方式在需要时在状态管理中“降级”。

我认为很明显,作为“低级”库 redux 已经取得了巨大的成功,而作为用于构建应用程序的功能丰富的框架,它还没有完全实现。 需要注意的一件事是,随着 redux 的发展,它不会破坏它作为 Apollo 等项目的行业标准“协议”的角色。

为此,或许现在的 redux 可以变成一个像redux-core那样的包,而redux本身就提升了抽象的层次,给出了一种推荐的构建应用的方式。 当然,使用 redux-core 可以有任何数量的更高级别的替代品来替代这个redux (比如 Apollo,或者使用 sagas 的东西?)...

重申事情的澄清和强调:

此线程中的任何工作都不会包括对现有 Redux 库的重大更改:) 完全可以对文档进行改造,并且可能会有一些小的调整,但现有的redux包将保持原样。

任何像我刚刚描述的redux-starter包,或者 Dan 概述的“框架”-y 方法,都将是一个依赖于redux的新包。

我投票决定我们像@markerikson建议的那样创建包。 一旦我们有了代码库,我们就可以开始围绕 api、代码片段和更深入的想法进行讨论。 然后,我们将有一个地方可以为特定功能创建问题。

您可以使用 Delux 作为启动器 (jk)

作为@tannerlinsley Jumpstate的重度用户(现在是维护者之一),我对此感到非常兴奋。 我使用 Jumpstate 的原因在@gaearon的原始帖子中非常简洁地概述。 话虽如此,这不是一个完整的解决方案(它从未打算成为)。 就在今天早上,我正在考虑可以在此基础上添加的一些内容,然后我发现了这个线程,发现你们已经在考虑它了。

冗长的说法:听起来很棒,我在,不确定我能贡献多少(我会尝试)但你肯定有一个啦啦队长。

我已经多次使用 Jumpstate 教授 Redux,并且可以自信地说,它大大缩短了学习曲线。 我认为 Redux 像 HTTP 和 Jumpstate 像 fetch。 我喜欢 Redux 并且用它写了一些(可笑的)复杂的东西,但我也知道 90% 的案例足以教大多数东西。

当你想到它时,这是一个很棒的地方。 关于前端开发和 javascript 的核心抱怨之一是“缺乏标准库”。 事情是,这是一个非常新的东西(TM) ,能够在浏览器中开发完整的应用程序。 需要标准库的压力终于上升了。 这就是我们现在所处的位置。

回复:学习曲线、自以为是和抽象:

Ajax 似乎是新学习者陷入困境的领域,无论他们是在学习使用 thunk、saga 还是史诗。 他们似乎没有太大困难的一个领域是创建动作——因为它们只是普通的旧 Javascript 对象,我们有 Flux 标准动作规范提供的额外清晰度。 这个思路让我想到了一个问题——为什么不开发一个Flux Standard Ajax Action

例如

function fetchPosts(page) {
  return {
    type: 'FETCH_POSTS_REQUEST',
    // Ajax request setup
    ajax: {
      url: `/api/posts`,
      method: 'GET',
      data: { page },

      // Optional ajax meta attributes
      meta: {
        // Amount of time to debounce the execution of the request
        debounce: 400,
        // Amount of times to retry the request on failure
        retry: 2,
        // Amount of time before timing out
        timeout: 10000,  
        // The action type that cancels the request
        cancelType: 'CANCEL_FETCH_POSTS',
        // Strategy when multiple FETCH_POSTS_REQUESTs are in flight
        resolve: 'LATEST'
      }
    }
  };  
}

ajax 动作将由中间件处理(它会对动作中的ajax键做出反应)。

我一直在想为什么这种方法是个坏主意,但还没有想到任何交易破坏者。
我估计有一些好处:

  • 平均应用程序所需的异步操作数量显着减少(因为与 ajax 相关的异步操作会消失)
  • 易于理解,因为我们只处理普通的旧 javascript 对象
  • 它是声明性的,实现细节并不重要
  • 不同的实现可以无破损地换入/换出
  • 它是惯用的 Redux(动作日志仍然有意义,可序列化等)

我可以听到一些人在调用关注点分离的论点,但我认为这是对我们的 ajax 请求操作已经在做的事情的自然扩展。 目前,他们正在设置一个具有非常清晰的上下文'FETCH_POSTS_REQUEST' ,但是上下文太少,无法抽象出 ajax 请求的实现。 几乎没有选择,只能在其他地方手动执行 ajax 请求。

我想到的另一件事是我在一些文档中看到的异步操作链。 我们不需要以这种方式链接请求,所以我不确定这种链接的使用范围有多广,但我的直觉是它超出了我在这里提出的范围(也许不不属于本讨论中提到的 90% 用例)。

总而言之 - 这样的抽象似乎很有可能有助于缓解学习曲线和编写常见 Redux 应用程序所需的代码量。 有没有人对这种方法有任何想法 - 您能想到的任何交易破坏者? @markerikson @gaearon

现有技术: redux-axios-middleware

在这个线程中发生了很多事情。

关于 Redux 复杂性的讨论。 我个人不同意人们抱怨 Redux 复杂和冗长。 在使用基于 Flux 和 Elm 架构的低级库时,这是一种权衡。 Redux 主页中说明您可能不需要它。 但是我们仍然有人抱怨它很复杂。

Redux 是按原样查找的。 幸运的是,核心团队意识到它是功能完备的并且不愿意改变它。 建立在它之上的抽象非常棒,每个抽象都有自己的用例。 这是您对设计良好且设计良好的低级库所期望的。

我认为问题不在于 Redux 本身,而在于缺乏渐进的、开箱即用的学习方式。 正如一些人提到的,React 本身也有同样的问题,而create-react-app是解决方案。

也许这甚至不是问题,因为有很棒的资源(即丹的蛋头系列)。 也许人们想要使用一个无需任何学习曲线即可解决复杂问题的库是问题所在。

无论如何,这不是该线程的内容。

@markerikson我个人愿意帮助开发这个redux-starter

_虽然我喜欢更酷的名字_:stuck_out_tongue:

产生许多动作所需的样板几乎是我在 redux 中看到的唯一缺点。

这是非常低级的,但我认为这很棒。 这就是为什么我更喜欢 Redux 风格的范式而不是 MobX 的原因之一,尽管我确实理解它的用处并且会使用 MobX。 但是,既然我了解 Redux,我就会很高兴这样做。 因为 Redux(或一般的 Redux 模式),数据并没有特别“隐藏在幕后”。 通常,无论在哪里处理代码,您都会编写它,如果没有,您可以理解它(如果您花时间学习,稍后会详细介绍)。

问题是,当我第一次学习 Redux 时,要理解很多,但也很简单。 事情可能既困难又简单。 然而正因为如此,我想,“哇,这个概念太合乎逻辑了!我敢打赌我自己能做到!” 因此,对于我的第一个项目,我没有使用 Redux 包,而是创建了自己的小型 Redux 商店。

这样做不仅帮助我理解了 Redux 的概念,而且让我对 Javascript 本身的理解增加了很多! (这是从一个在此之前对函数式编程概念几乎一无所知的人的角度来看的,所以你的里程可能会有所不同。)Redux 的概念在它的简单性方面确实非常出色,但也非常强大。

我认为问题是因为 Redux 如此受欢迎,所以,呃,“流行语”可以分散新手对它在 Javascript 中的工作原理的注意力。 所以他们的心态可能是“好吧,我有了 React、Redux 等,现在我要成为了不起的程序员并制作了很棒的应用程序!” 并不是说这有什么问题。 但是他们可能只想使用一个闪亮的新工具来加快开发过程,而不了解是什么使该工具起作用。

一旦理解了这个概念,就很容易让 Redux(或任何类似 Redux 的模式)做各种各样的事情。 我喜欢它的原因与我喜欢 React 而非其他(仍然很棒)工具(如 Angular)的原因相同。 _这只是 Javascript。_ 这就是 Javascript 的工作原理!
如果可以通过某种范式减少必要的样板,那就太好了,但我认为为无限制的 powaaaaaa 付出的代价很小。

IMO 所有这些“冗长是一种权衡”只是受虐狂; 冗长不是任何东西的服务,这是你让所有使用Redux的牺牲。 我不是在责怪 Redux 或 javascript,我只是想指出这不是必要的复杂性。 Elm 用更少的行和更简单的代码管理 Redux 实现的一切。

所以我们机械地写出样板。 Jumpstate 和类似的东西让生活更轻松,但是你在运行时可以做的事情是有限的,并且使用 typescript/flow jumpstate 并没有多大帮助。 但是代码生成很困难,没有人想在他们已经负担过重的构建系统中再添加一件事。

如果我能有像 Elm 代码这样的东西被翻译成惯用的 Redux ......仍然是我跳动的心。

以下是我的一些想法,希望我不要重复以前的评论:

动作创建者可以用作动作类型

动作.js

export function increase() {}

减速器.js

import { increase } from './actions.js';
export default handleActions({
    [increase]: (state) => state + 1
}, 0);

由于函数具有唯一的引用,我们可以将它们视为动作的标识符。 与 FSA 不兼容,但可以节省大量代码并防止错误。

动作可以异步调度

async function a() {

}
async function b() {
}
store.dispatch(a());
store.dispatch(b()); // b() will be dispatched after a() resolves

这样,动作创建者可以是异步函数,然后一个接一个地分派。 正如我在许多情况下看到的那样,人们不需要使用基于中间件的解决方案,它将节省大量样板代码。
这个概念是由Delux 的一个 promise factory 实现的

redux === 样板

https://github.com/Emilios1995/redux-create-module

但我认为这个功能可以完全挽救你的生命。

值得区分此线程中正在进行的两组不同的想法:

  • 一个“全方位服务”的基于 redux 的框架
  • redux 核心中的附加功能

我同意@markerikson 的观点,即 redux 核心本身不应该成为一个框架,而且我理解为什么人们希望将 redux-thunk 和 react-redux 保留在单独的包中。 但是,我相信 redux 核心中的新“辅助函数”既有空间也有先例。

Redux 具有相当小的 API 表面积: createStoreapplyMiddlewarecombineReducersbindActionCreatorscompose 。 但其中大部分是便利功能。 只有createStore是 Redux 工作所必需的。 为什么这些都属于核心?

  • applyMiddleware创建了一个简化的界面来存储增强器。 但更重要的是, applyMiddleware支持中间件 _interface_ 作为向 redux 存储添加效果的标准方式。 中间件生态系统是可能的,因为applyMiddleware有一个相对简单的界面,并且允许用户同时运行多个中间件。

  • combineReducers提供了一种方便的 reducer 组合形式。 它远不是组合减速器的唯一方法,但它无疑是更有用的模式之一。 但是它在 redux 核心中的存在意味着它不仅很常见而且_ubiquitous_。 我遇到的每个重要的 Redux 应用程序都使用combineReducers来创建根减速器。 另一面是,因为combineReducers是 Redux 中唯一的 reducer 组合形式,许多用户似乎相信它是唯一存在的 reducer 组合形式。

  • bindActionCreators减少了一些围绕调度动作的样板。 但反常的是, bindActionCreators也间接导致了 Redux 在样板方面的一些声誉。 这是因为仅仅存在bindActionCreators鼓励使用动作创建器。 当人们比较动作创建者(由bindActionCreators “认可”)与选择器(已记录但没有等效的核心功能)的流行时,这一点尤其明显。 我见过许多使用react-redux代码库,其中mapStateToProps始终是“定制”选择器,但mapDispatchToProps始终是预先编写的动作创建者的对象映射。

这些都不是 Redux 的功能所必需的,但所有这些都对其可用性至关重要。 这些函数都没有限制 Redux 的固有灵活性,但是通过将常见模式具体化为可调用函数,它们使选择变得不那么令人生畏。 这些辅助函数中的每一个都提出了进一步的问题:

  • 如果中间件是商店增强器的简化,是否还有其他可以广泛使用的简化?
  • 可以实现哪些其他形式的 reducer 组合而不是仅仅记录? 减速器对象? 依赖减速器? 线性组合?
  • 集成reselect会导致人们在以前没有的地方创建和组合选择器吗? 这会是一个净正值吗?

即使所有这些的答案都是否定的,我认为我们甚至拒绝考虑他们的前提对我们自己和社区都有害。

@modernserf :首先,感谢您的评论,以及您最近对 Redux 的其他想法。 它们都是 _extremely_ 有价值的和信息量大的,并且可能应该要求本线程中的其他人阅读。 因此,我将在此处链接它们:

  • 主帖: Redux 有什么了不起?
  • 导致该帖子的 Twitter 线程的强化版本: https ://storify.com/acemarke/redux-pros-cons-and-limitations
  • 之后的一些额外想法: https :

非常有趣的是,你应该提出这些,因为涉及的历史(我刚刚花了很多时间研究我的帖子The Tao of Redux, Part 1 - Implementation and Intent 。特别是, redux-thunk是最初内置于 Redux 中,而react-redux也是后来才分离出来的

您说得对,这些实用程序确实编纂了某些使用模式。 也就是说,包含这些实用程序是因为它们编写了 _intended_ 使用模式:切片缩减器的组合、异步行为和其他集中行为的中间件管道,以及绑定动作创建者以简化从组件分派动作的过程(特别是传递函数到子组件)。 有点类似,虽然 Reselect 不在核心中,但Dan 确实明确鼓励它的创建

所以是的,任何人“都可以”编写这些,但是通过构建它们,我们推动人们以特定方式使用这些特定工​​具。 绝对同意这一点。

根据您的最后三个问题:

  • 我猜你可以说中间件是“商店增强器的简化”,尽管我认为这不公平。 相反,它是一种用于非常专业目的的商店增强剂。 至于其他存储增强器相关的工作:Dan 说过,他仍然想修改的 Redux 核心的一个主要部分与增强器的初始化方式有关。 实际上,有两个“竞争的”永久居民,以改变商店增强实现,一直坐在开了很长一段时间:@acdlite的版本#1706,和@jimbolla的版本#2214。 有趣的是,Jim 整理了所有已知商店增强器的列表,以帮助分析社区实际如何使用它们。 我知道 Jim 的提议旨在帮助增强器作者简化常见的行为。 所以,这是一个可以进一步探索的方向。
  • 肯定有很多其他的减速器组合和结构模式。 我不确定就“依赖”reducer 而言我们到底能做什么,因为那是更小众的用例,而且似乎非常特定于应用程序。 在相关的说明中,很长一段时间内,Dan 关闭了任何提议向combineReducers添加某种第三个参数的 PR(并且已经有 _lot_ 个),但最终允许 #1768 继续推进尽管。 该公关也停滞不前。
  • 选择器最常见的用例似乎是直接提取状态位并将它们作为命名道具传递。 我们还为 React-Redux 提供了一个公开 PR,用于mapState添加对象速记语法,并且有很多请求,即使您可以使用 Reselect 的createStructuredSelector做同样的事情。

正如我在“Redux 之道”帖子中所记录的那样,声明的目标是使 Redux 核心 API 尽可能最小化,并鼓励在其之上建立一个生态系统。 此外,在 Redux 开发的早期,Andrew发表了评论,表示“祝福”某些插件的意图

正如 gaearon 曾经说过的,(我不记得在哪里......可能是 Slack)我们的目标是像 Flux 库中的 Koa。 最终,一旦社区更加成熟,计划是维护一个“祝福”插件和扩展的集合,可能在一个 reduxjs GitHub 组织下。

早些时候,我提出了某种“简单的 Redux 设置”抽象库,或者类似的东西。 像这样的东西,或者至少是“祝福”插件列表,是否足够符合您的想法? (而且,实际上,您在本主题的第一条评论中也这样做了。)如果没有,还有其他建议或想法吗?

最终,一旦社区更加成熟,计划是维护一个“祝福”插件和扩展的集合,可能在一个 reduxjs GitHub 组织下。

这也是我会做的,尽管我可能会更进一步:我会将项目重组为一个 monorepo,其中当前的redux包变成类似于@redux/core ,祝福像 reselect 和 redux-action 这样的库被引入到 repo 和命名空间中,而redux包本身只是将所有这些包重新导出到一个命名空间中。

那 _sort of_ 满足不修改 redux 核心的约束。 此外,扩展版本将是核心的严格超集——因此向后兼容——核心。 新功能将完全是可选的,并且没有对私有状态的特殊访问权限。 就“膨胀”而言——这些库中的每一个开始时都很小,而且它们的编写方式使得对它们进行摇树摇晃是微不足道的。

我们还为 React-Redux 提供了一个公开 PR,用于为 mapState 添加对象速记语法,并且有很多请求,即使您可以使用 Reselect 的createStructuredSelector做同样的事情。

我想这应该告诉你一些事情。

我建议的 monorepo 结构基本上是 Lodash 使用的结构。 现在——Lodash 是一个有趣的模型库:它是一个庞大的函数集合,其中许多是很容易实现的。 几乎 lodash 中的每个函数都可以更灵活地作为“配方”——毕竟groupBy确实是一个 _pattern_; 我们有什么资格说你在什么数据结构上使用它?

每个 lodash 函数也可以在自己的 _a la carte_ 库中使用。 import { groupBy } from "lodash"没有 _functional_ 好处超过import groupBy from "group-by" 。 当我可以安装我需要的东西时,为什么还要处理 tree-shaking?

但是使用 Lodash 的体验与使用配方或纳米库的体验有着根本的不同。 您知道,如果您正在使用数组做某事并且感觉像是在重复自己,那么可能有一个 Lodash 函数可以做到这一点。 您不需要查找配方,也不需要安装任何东西——您甚至不需要向文件添加另一个导入行。 它只消除了最微小的摩擦,但我们的日子充满了这样的时刻,而且它加起来。

我希望你不介意我从不同的角度加上我自己的 2 美分。

CQRS事件溯源和最终的领域驱动设计思想流派是我们产生FluxRedux的更伟大的祖先。 虽然偶尔会引用这些影响,但Redux及其后代工具从当时流行的FluxFlux实现中获取了大部分 API 术语。 这很好,但我认为我们错过了一些机会,可以从开发人员可能不得不接受这些想法的现有曝光中获利。

举两个不同的例子:在我见过的大多数事件溯源讨论中, Redux Actions将被称为EventsRedux ActionCreators将被称为Commands 。 我认为这个术语也免费提供了一些额外的清晰度和语义。 事件是可记录的、可序列化的数据。 事件描述了过去时发生的事情。 事件可以重播。 事件不能取消记录。 命令具有命令触发事件

我怀疑事件溯源作为一种架构模式将在未来十年内越来越流行,我认为Redux可以通过使 API 和讨论与更广泛的社区更紧密地保持一致来获得一些可用性、清晰度和易用性。

我觉得这个讨论已经被打死了: https :

哦,我不知道,谢谢! :+1:

看起来#351 已关闭,因为当时无法采取任何措施。 如果我们正处于重新考虑 API 决策和我们使用的语言的关头,对我来说,这似乎是重新提出这个想法的合适时机。

我可以更具体。

这是我对受事件溯源习语启发的 API 可能是什么样子的想法:

概念

  • 可观察事件流
  • 事件流具有一组类型良好的受支持事件
  • 命令分派到事件流
  • 一个或多个客户端使用事件流

应用程序接口

import { createEventStream, createProjection } from 'redux';

// Initialize the event stream separately from the store.  This becomes the one
// true source of truth for your application.
const eventStream = createEventStream({
  // Commands are the only thing that we want to couple to the eventStream.  The 
  // set of events which may end up in an eventStream should be easy to predict.
  //
  // A definition like this supports static analysis inference well for 
  // consumers that can leverage it.
  increment: () => ({ type: 'INCREMENT' }),
  decrement: () => ({ type: 'DECREMENT' }),
});

// Multiple stores with disjoint or overlapping data can be used to consume the 
// same event stream.
const store = createProjection(eventStream, reducer, init);
const adminStore = createProjection(eventStream, adminReducer, init);

// We don't need a jargon term ("Middleware"), or a dedicated hook to handle 
// async anymore.  We just register more subscribers to the eventStream.
eventStream.subscribe(myCustomMiddleWare);
eventStream.subscribe(sendEventsToAnalytics);
eventStream.subscribe(logEventsForPlayback);

// Calls to commands can be wrapped with React Providers or container components 
// in the same way that Redux currently does.  They can also be called directly.
eventStream.increment();

@ajhyndman :虽然我很欣赏这些建议和讨论,但那个自行车棚已经完全被粉刷了,马已经逃离了谷仓(完全混合隐喻)。 Dan 选择使用 Flux 术语而不是 CQRS/ES 术语,并且没有计划更改核心 API 或用于 Redux 概念的术语。

(此外,虽然我有零统计数据来证实这一点,但我猜想在这一点上,至少在 JS 社区中,熟悉 Redux 使用“动作”和“动作创建者”的人比熟悉 CQRS/ES 术语的人要多。 )

我绝对有这样的印象,这种对话以前已经发生过,并且强烈希望不要重新打开它。 😄我不会在这里用力过猛。 (如果有更好的接触点,我有兴趣在其他地方阅读或继续这个对话。)

当然,您也说对了,有一个根深蒂固的因素,在这个时候改变整个 API 表面和术语将是昂贵的。

我仍然认为Event SourcingRedux社区有机会相互学习。

  • 可以创建哪些可能的抽象来简化学习和使用过程,但又不会真正隐藏 Redux(并希望提供一个迁移/学习路径到“基础”Redux)?

即使不借用术语或更改 API,我认为我们还是可以找到胜利的。 例如,我成功地将商店描述为“一个状态容器,其中 currentState 是”(它派生自或减少自)仅附加列表(或流)的“纯函数”,从而成功地向人们介绍了Redux行动”。

展望未来,我认为完全有可能实现和抽象一个类似 redux 的服务器基础设施(参见: Apache Samza和 LinkedIn 工程团队的一些其他工作)。 如果我们渴望这一点,那么也应该可以对客户端和服务器状态管理使用类似的抽象! 如果我们能做到这一点,为什么不使用同构的、持久的Redux存储呢?

JavaScript 和基础设施社区能够结合在一起或轻松映射的任何概念,我预计这些机会越快变得明显,抽象就越具有表现力。

我想我只是对其中一些想法有点兴奋! 我希望你能原谅我用倾倒这个词。

旁注:似乎可以为 Redux 创建一个类似事件流的包装器 API,它实现了我上面建议的表面!

https://github.com/ajhyndman/redux-event-stream

@ajhyndman我认为包装器是实现您的想法的正确方法👍

与您提到的 samza 和 linkedin 工程工作相关,如果其他人还没有阅读/观看精彩的演讲用 Apache Samza 翻转数据库,那么请找一个小时的时间这样做! 我想我在某个时候看到 dan 在自述文件或推文中提到了它,这个演讲永远改变了对数据库和状态管理的看法,并且是 redux 的一个非常接近的思想表亲。

那个视频太棒了! 它实际上也是redux回购自述文件中感谢列表中的第二项。

嘿大家!

在不知道该线程存在的情况下,我开发了一个库,它在某种程度上直接回答了@markerikson原始问题,并从@gaearon列表中删除了大部分内容。

该库名为Kea

screen shot 2017-08-06 at 13 10 03

它是对 redux、redux-saga 和 reselect 的抽象。 除了将它们连接在一起的胶水之外,Kea 不会假装创造任何新东西,而是根据需要公开原始的 redux action creators 和 reducer、raw redux-saga sagas 和 raw reselect selectors。 它不会发明新概念。

Kea 支持许多不同的操作模式:你可以独立使用它,连接到 React 组件,内联在 React 组件之上,甚至动态连接到 React 组件,其中选择器的输入取决于 React 组件的 props。

我个人将它与两个大型应用程序一起使用:一个是私人课程市场,另一个是巨大的车队跟踪软件。 另外,我知道有人用它构建了自己的应用程序。 因为它对我们非常有用,所以我花了大量时间清理它并编写文档

对我来说,这个库总是大大减少样板,同时仍然忠于 redux 本身的核心。

不管怎样,废话不多说,这里有一些代码。 这是我所说的“内联 kea”的一个示例——逻辑直接附加到您的组件。 这种模式非常适合刚开始使用或作为 React 的setState的替代方案。

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { kea } from 'kea'

@kea({
  actions: () => ({
    increment: (amount) => ({ amount }),
    decrement: (amount) => ({ amount })
  }),

  reducers: ({ actions }) => ({
    counter: [0, PropTypes.number, {
      [actions.increment]: (state, payload) => state + payload.amount,
      [actions.decrement]: (state, payload) => state - payload.amount
    }]
  }),

  selectors: ({ selectors }) => ({
    doubleCounter: [
      () => [selectors.counter],
      (counter) => counter * 2,
      PropTypes.number
    ]
  })
})
export default class Counter extends Component {
  render () {
    const { counter, doubleCounter } = this.props
    const { increment, decrement } = this.actions

    return (
      <div className='kea-counter'>
        Count: {counter}<br />
        Doublecount: {doubleCounter}<br />
        <button onClick={() => increment(1)}>Increment</button>
        <button onClick={() => decrement(1)}>Decrement</button>
      </div>
    )
  }
}

如果您的应用程序增长并且更多地方需要访问incrementdecrement操作或counter道具,您可以像这样拆分代码:

// counter-logic.js
import PropTypes from 'prop-types'
import { kea } from 'kea'

export default kea({
  actions: () => ({
    increment: (amount) => ({ amount }),
    decrement: (amount) => ({ amount })
  }),

  reducers: ({ actions }) => ({
    counter: [0, PropTypes.number, {
      [actions.increment]: (state, payload) => state + payload.amount,
      [actions.decrement]: (state, payload) => state - payload.amount
    }]
  }),

  selectors: ({ selectors }) => ({
    doubleCounter: [
      () => [selectors.counter],
      (counter) => counter * 2,
      PropTypes.number
    ]
  })
})
// index.js
import React, { Component } from 'react'
import { connect } from 'kea'

import counterLogic from './counter-logic'

@connect({
  actions: [
    counterLogic, [
      'increment',
      'decrement'
    ]
  ],
  props: [
    counterLogic, [
      'counter',
      'doubleCounter'
    ]
  ]
})
export default class Counter extends Component {
  render () {
    const { counter, doubleCounter } = this.props
    const { increment, decrement } = this.actions

    return (
      <div className='kea-counter'>
        Count: {counter}<br />
        Doublecount: {doubleCounter}<br />
        <button onClick={() => increment(1)}>Increment</button>
        <button onClick={() => decrement(1)}>Decrement</button>
      </div>
    )
  }
}

请尝试并阅读文档以通过 redux-saga 和您可以使用的其他功能了解更多有关副作用的信息。

到目前为止,对 Kea 的反馈非常积极,引用了一个问题:“更多的人应该使用 KeaJS,因为它让 redux 世界变得更美好!👍”:)

感谢您抽出时间阅读本文! :)

看起来不错! 虽然'increment'不是很喜欢魔法字符串

这个讨论是否在另一个地方进行了? Kea 是公认的最正确的结果吗?

@lucfranken :不,讨论失败了。

如果您正在寻找 Redux 之上的更高级别的抽象,那么 Kea 可能是一个不错的选择。 如果您正在寻找可简化商店设置过程的“Redux 入门包”,您可能需要四处看看。 我知道我在野外见过一些类似的东西,但我们现在没有任何这样的官方库。

把我的帽子扔在这里。 这就是我解决这个问题的方法: https :

我个人认为这是对样板的极大简化和减少。

这就是我如何构建我最近构建的所有东西。

猜猜它会有所帮助:
https://github.com/anish000kumar/redux-box
image

嘿,在我的公司,我们刚刚开源了一个管理网络层的库,并从 Redux 中删除了很多样板。 事实证明它对我们来说是成功的,但请自己尝试一下: https :

1_327nnvo3cuaqc-dsqpoq7w

这是我减少 redux 样板文件的方法
image

https://github.com/zhDmitry/restate

作为对线程的一个迟来的死亡,不久前我在 Twitter 上询问了“样板”对人们意味着什么的反馈:

https://twitter.com/acemarke/status/969040835508604929

死灵线回归!

根据我对该线程中第一条评论的编辑,此处讨论的想法已转化为我们的Redux-Starter-Kit 包。 请尝试一下,看看它对简化您的 Redux 使用有多大帮助!

此页面是否有帮助?
0 / 5 - 0 等级