Remath: Redux 的重新设计

2019-05-15 admin

难道现在状态管理不是一个可以解决的问题吗?直观地说,开发人员似乎知道一个隐藏的事实:状态管理的使用似乎比需要的更困难。在本文中,我们将探讨一些你可能一直在问自己的问题:

  • 你是否需要一个用于状态管理的库?
  • Redux 的受欢迎程度是否值得我们去使用? 为什么或者为什么不值得?
  • 我们能否制定更好状态管理解决方案吗?如果能,要怎么做?

状态管理需要一个库吗

作为前端开发人员,不仅仅是布局,开发的真正艺术之一是知道如何管理存储状态。简而言之:状态管理是复杂的,但又并非那么复杂。

让我们看看使用React等基于组件的视图框架/库时的选项:

clipboard.png

1. Component State (组件状态)

存在于单个组件内部的状态。在React中,通过setState方法更新state

2. Relative State (关联状态)

从父级传递给子级的状态。在React中,将 props 作为属性传递给子组件。

3. Provided State (供给状态)

状态保存在根 provider (提供者) 组件中,并由 consumer (消费者) 在组件树的某个地方访问,而不考虑组件之间的层级关系。在 React 中,通过 context API 可以实现。

大多数的状态都是存在于视图中的,因为它是用来反映用户界面的。那么,对于反映底层数据和逻辑的其它状态,又属于谁呢?

将所有内容都放在视图中可能会导致关注点的分离:它将与javascript视图库联系在一起,使代码更难测试,而且可能最大的麻烦是:必须不断地思考和调整存储状态的位置。

状态管理由于设计变更而变得复杂,而且通常很难判断哪些组件需要哪些状态。最直接的选择是从根组件提供所有状态,如果真要这么做的话,那么选用下一种方式会更好。

4. External State (外部状态)

状态可以移出视图库。然后,库可以使用提供者/消费者模式连接以保持同步。

也许最流行的状态管理库是Redux。在过去的两年里,它变得越来越受欢迎。那么为什么这么喜欢一个简单的库呢?

Redux 更具性能?答案是否定的。事实上,为了每一个必须处理的新动作(action),都会稍微慢一些。

Redux是否更简单?当然不是。

简单应当是纯javascript:比如 TJ Holowaychuk 在twitter上说

clipboard.png

那么为什么不是每个人都使用 global.state={}?

为什么使用 Redux

在表层之下,Redux 与 TJ 的根对象{}完全相同——只是包装在了一系列实用工具的管道(pipeline)中。

clipboard.png

在 Redux 中,不能直接修改状态。只有一种方法:**派发(Dispatch)一个动作(Action)**到管道中,管道会自动根据动作去更新状态。

沿着管道有两组侦听器:中间件(middleware)订阅(subscriptions)。 中间件是可以侦听传入的动作的函数,支持诸如“logger”,“devtools”或“syncWithServer”侦听器之类的工具。 订阅是用于广播这些状态更改的函数。

最后,**合成器(Reducer)**函数负责把状态变更拆分成更小、更模块化、更容易管理的代码块。

和使用一个全局对象相比,Redux 确实简化了开发过程。

将 Redux 视为一个带有更新前/更新后钩子的全局对象,以及能够以简单的方式合成新状态。

Redux 是不是太复杂了?

是的。有几个不可否认的迹象表明 API 需要改进,这些可以用下面的方程来总结

clipboard.png

time_saved来表示你开发自己的解决方案所花费的时间,time_invested相当于阅读文档,学习教程和研究不熟悉的概念所花费的时间。

Redux 是一个拥有陡峭学习曲线的小型库。虽然有不少开发者能够克服深入学习函数式编程的困难并从 Redux 获益良多,但是也有很多开发者望而却步,宁愿重新使用 jQuery。

使用jQuery你不需要理解“monad”是什么,你也不需要为了使用Redux去理解函数组合。

使用 jQuery 你不需要理解“comonad”是什么,你也不需要为了使用 Redux 去理解函数组合。

任何框架或者库的目的都应该是把复杂的事物抽象得更加简单。

重新设计Redux

我认为Redux值得重写,至少有以下 6 个方面可以改进得更友好。

1.初始化

让我们来看看一个基本的 Redux 初始化过程,如下图左边所示:

clipboard.png

许多开发人员在第一步后就在这里暂停,茫然地盯着深渊。 什么是 thunkcompose?一个函数能做到这些吗?

如果 Redux 是基于配置而不是函数组合的话,那么像右边那样的初始化过程明显看起来更加合理。

2. 简化 reducers

Redux 中的 reducers 可以通过一个转换,让我们远离已经习惯但不必要且冗长的 switch 语句。

clipboard.png

假设reduceraction类型匹配,那么我们可以对参数进行反转,这样每个reducer都是一个接受stateaction的纯函数。 也许更简单,我们可以标准化action并仅传入state和有效负载(payload)。

3.使用 Async/Await 代替 Thunks

thunk 通常用于在 Redux 中创建异步 action。 在许多方面,thunk 的工作方式看起来更像是一个聪明的黑客,而不是官方推荐的解决方案。 我们一步一步来看:

  1. 你派发一个action(dispatch an action),它实际上是一个函数而不是预期的对象。
  2. thunk 中间件检查每个动作,看看它是否是一个函数。
  3. 如果是,中间件调用该函数,并传入一些 store 的方法:dispatchgetState

怎么会这样?一个简单的 action 到底是作为一个动态类型的对象、一个函数,还是一个 Promise?这难道不是一种拙劣的实践吗?

clipboard.png

如上图右边所示,难道我们就不能只使用 async/await ?

4. 两种 action

仔细想想,其实有两种 action

1.reducer action: 触发 reducer 并改变状态。

2.effect action:触发异步 action,这可能会调用reducer操作,但异步函数不会直接更改任何状态。

将这两种类型的 action 区分开来,将比上面的thunk用法更有帮助,也更容易理解。

5. 不再有 action 类型(action.type)变量

为什么我们的标准实践要把 action creator 和 reducer 区分开来呢?能否只用其中一个呢?改变其中一个又是否会影响到另一个?

action creator 和 reducer 是同一枚硬币的两面。

**const ACTION_ONE = ‘ACTION_ONE’**是分离 action creators 和 reducers 的一个冗余产物。应将两者视为一体,并且不再需要文件导出类型的字符串。

6.reducers 即 action creators

按照使用方式,把 Redux 中所涉及的概念进行合并分组,那么我们可以得出下面这个更简单的模式。

clipboard.png

可以从 reducer 中自动确定 action creator。 毕竟,在这种情况下,reducer 可以成为action creator

使用一个基本的命名约定,下面是可预测的:

  1. 如果 reducer 命名为 increment,那么 type 就是 increment。更好的做法是加上命名空间 “count/increment”
  2. 每个 action 都通过 payload 键来传递数据。

clipboard.png

现在,从 count.increment 中,我们可以以一个 reducer 生成 action creator。

好消息:我们可以有一个更好的 Redux

以上这些痛点就是我们创建 Rematch 的原因。

clipboard.png

Rematch 对 Redux 进行了封装,提供更简单的 API,但又不失任何可配置性的特点

clipboard.png

请参见下面的一个完整的 Rematch 示例:

clipboard.png

在过去的几个月里,我一直在实际业务中使用 Rematch。作为证明,我会说:状态管理从未变得如此简单、高效。

Redux 与 Rematch 的对比

Redux 是一个出色的状态管理工具,有键全的中间件生态与出色的开发工具。

<a>Rematch</a> 在 Redux 的基础上构建并减少了样板代码和执行了一些最佳实践。

说得清楚点,Rematch 移除了 Redux 所需要的这些东西:

  • 声明 action 类型
  • action 创建函数
  • thunks
  • store 配置
  • mapDispatchToProps
  • sagas

让 Redux 与Rematch 作对比有助于让理解更加清晰。

Rematch

1.model

import { init } from '@rematch/core'

const count = {
  state: 0,
  reducers: {
    upBy: (state, payload) => state + payload
  }
}

init({
  model: { count }
})

2.View

import { connect } from 'react-redux'

// Component

const mapStateToProps = (state) => ({
  count: state.count
})

const mapDispatchToProps = (dispatch) => ({
  countUpBy: dispatch.count.upBy
})

connect(mapStateToProps, mapDispatchToProps)(Component)

Redux (最佳实践)

1.store

import { createStore, combineReducers } from 'redux'
// devtools, reducers, middleware, etc.
export default createStore(reducers, initialState, enhancers)

2.Action Type

export const COUNT_UP_BY = 'COUNT_UP_BY'

3.Action Creator

import { COUNT_UP_BY } from '../types/counter'

export const countUpBy = (value) => ({
  type: COUNT_UP_BY,
  payload: value,
})

4.Reducer

import { COUNT_UP_BY } from '../types/counter'

const initialState = 0

export default (state = initialState, action) => {
  switch (action.type) {
    case COUNT_UP_BY:
      return state + action.payload
    default: return state
  }
}

5.view

import { countUpBy } from '../actions/count'
import { connect } from 'react-redux'

// Component

const mapStateToProps = (state) => ({
  count: state.count,
})

connect(mapStateToProps, { countUpBy })(Component)

Rudex 与 Rematch 的分数板

clipboard.png

Redux 并没有被抛弃,而且也不应该被抛弃。

只是,我们应该以更低的学习成本,更少的样板代码和更少的认知成本,来拥抱 Redux 背后的简单哲学。

你的点赞是我持续分享好东西的动力,欢迎点赞!

欢迎加入前端大家庭,里面会经常分享一些技术资源。

clipboard.png

[转载]原文链接:https://segmentfault.com/a/1190000019056045

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-65037.html

文章标题:Remath: Redux 的重新设计

相关文章
10个强大的纯CSS3动画案例分享
我们的网页外观主要由CSS控制,编写CSS代码可以任意改变我们的网页布局以及网页内容的样式。CSS3的出现,更是可以让网页增添了不少动画元素,让我们的网页变得更加生动有趣,并且更易于交互。本文分享了10个非常炫酷的CSS3动画案例,希望大家...
2015-11-16
v-charts | 饿了么团队开源的基于 Vue 和 ECharts 的图表工具
在使用echarts生成图表时,经常需要做繁琐的数据类型转化、修改复杂的配置项,v-charts的出现正是为了解决这个 痛点。基于Vue2.0和echarts封装的v-charts图表组件,只需要统一提供一种对前后端都友好的数据格式 设置简...
2018-05-24
从2014年的发展来展望JS的未来将会如何
&lt;font face=&quot;寰�杞�闆呴粦, Arial, sans-serif &quot;&gt;2014骞达紝杞�浠惰�屼笟鍙戝睍杩呴€燂紝鍚勭�嶈��瑷€灞傚嚭涓嶇┓锛屼互婊¤冻鐢ㄦ埛涓嶆柇鍙樺寲鐨勯渶姹傘€傝繖浜涜��...
2015-11-12
12个你未必知道的CSS小知识
虽然CSS并不是一种很复杂的技术,但就算你是一个使用CSS多年的高手,仍然会有很多CSS用法/属性/属性值你从来没使用过,甚至从来没听说过。 1.CSS的color属性并非只能用于文本显示 对于CSS的color属性,相信所有Web开发人员...
2015-11-12
ajax为什么令人惊异?ajax的优缺点
使用Ajax的最大优点,就是能在不更新整个页面的前提下维护数据。这使得Web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变的信息。 Ajax不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。就像DHT...
2015-11-12
HTML5的5个不错的开发工具推荐
HTML5规范终于在今年正式定稿,对于从事多年HTML5开发的人员来说绝对是一个重大新闻。数字天堂董事长,DCloud CEO王安也发表了文章,从开发者和用户两个角度分析了HTML对两个人群的优势。其实,关于HTML5的开发工具,我们以往的...
2015-11-12
JavaScript教程:JS中的原型
Keith Peters 几年前发表的一篇博文,关于学习没有“new”的世界,其中解释了使用原型继承代替构造函数。两者都是纯粹的原型编码。 标准方法(The Standard Way) 一直以来,我们学习的在 JavaScript 里创建对...
2015-11-12
AJAX的浏览器支持
AJAX 的要点是 XMLHttpRequest 对象。 不同的浏览器创建 XMLHttpRequest 对象的方法是有差异的。 IE 浏览器使用 ActiveXObject,而其他的浏览器使用名为 XMLHttpRequest 的 Jav...
2015-11-12
typeof、instanceof和contructor的区别
typeof:以字符串的形式返回变量的原始类型,typeof在两种情况下会返回&quot;undefined&quot;:一个变量没有被声明的时候,和一个变量的值是undefined的时候,注意,typeof null也会返回object,...
2015-11-12
JavaScript的组成
一个完整的JavaScript由3个部分组成:核心(ECMAScript) 文档对象模型(DOM) 浏览器对象模型(BOM) ECMAScript 描述了该语言的语法和基本对象 ; DOM 描述了处理网页内容的方法和接口 ; BOM 描...
2015-11-12
回到顶部