理解Javascript的状态容器Redux-阿里云开发者社区

开发者社区> 技术小甜> 正文

理解Javascript的状态容器Redux

简介:
+关注继续查看

Redux要解决什么问题?

随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态)。 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。
管理不断变化的 state 非常困难。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。state 在什么时候,由于什么原因,如何变化已然不受控制。 当系统变得错综复杂的时候,想重现问题或者添加新功能就会变得举步维艰。
Redux的设计借鉴Flux、 Elm、Immutable,它的出现就是为了解决state里的数据问题。Redux和Flux的主要区别是Redux里面是单一的数据源设计,而Flux(或者Reflux)里面有多个数据源。

Redux是如何工作的?

我们知道,在React中,数据在组件中是单向流动的。数据从一个方向父组件流向子组件(通过props),由于这个特征,两个非父子关系的组件(或者称作兄弟组件)之间的通信并不是那么清楚。
React并不建议直接采用组件到组件的通信方式,尽管它有一些特性可以支持这么做(比如先将子组件的值传递给父组件,然后再由父组件在分发给指定的子组件)。这被很多人认为是糟糕的实践方式,因为这样的方式容易出错而且会让代码向“拉面”一样不容易理解。
当然React也没有直接建议如何去处理这种情形,以下是React的文档中关于这部分的描述:

对于非父子关系的组件,你可以自己建立一个全局的事件系统,Flux的模式也是一种可行的方式。

Redux的出现就让这个问题的解决变得更加方便了。Redux提供一种存储整个应用状态到一个地方的解决方案(可以理解为统一状态层),称为“store”,组件将状态的变化转发通知(dispatch)给store,而不是直接通知其它的组件。组件内部依赖的state的变化情况可以通过订阅store来实现。
使用Redux,所有的组件都从store里面获取它们依赖的state,同时也需要将state的变化告知store。组件不需要关注在这个store里面其它组件的state的变化情况,Redux让数据流变得更加简单。这种思想最初来自Flux,它是一种和React相同的单向数据流的设计模式。
1030776-20170112022416166-631450629.jpg

Redux的核心设计理念

Redux有三大原则

  • 单一数据源,整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中

  • State 是只读的,惟一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。

  • 使用纯函数来执行修改,为了描述 action 如何改变 state tree ,你需要编写 reducers。

单一数据源的设计让React的组件之间的通信更加方便,同时也便于状态的统一管理。
根据Redux的文档,状态变化的唯一方式是触发一个action(一个可以描述发生了什么的对象),这意味着我们不能直接的去修改状态,取而代之的是我们可以通过转发action去告诉store我们有改变状态的意图。store对象提供了非常少的API,仅仅只有4个方法:

store.dispatch(action)store.subscribe(listener)store.getState()replaceReducer(nextReducer)

通过这几个API不难发现,store并没有直接提供setState()方法。
另外,由于它大量使用 pure function 和 plain object 等概念(reducer 和 action creator 是 pure function,state 和 action 是 plain object)这对于项目的稳定性会是非常好的保证。

理解Action、Reducer

一个action的例子:

var action = {
  type: 'ADD_USER',
  user: {name: 'Dan'}};// 假设store对象已经通过Redux.createStore()创建store.dispatch(action);

这段代码中,通过dispatch() 方法将action传递给了store。Action 本质上是 JavaScript 普通对象。我们约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作。多数情况下,type 会被定义成字符串常量。当应用规模越来越大时,建议使用单独的模块或文件来存放 action。

import { ADD_TODO, REMOVE_TODO } from '../actionTypes'

前面描述过,Redux不允许直接去改变state,必须通过转发action来告诉store有这个意图要去改变这个状态。reducer正是扮演处理转发过来的action的意图的函数并且可以改变状态的角色。一个reducer接受当前的state作为参数,通过返回新的state去改变原有的state:

var someReducer = function(state, action) {
  ...  return state;}

由于reducer是纯函数,需要注意:

  • 不允许在reducer函数内部进行网络调用或者数据库查询操作

  • 不能改变它的参数
    这样做的好处是:每次调用同样的一个函数,传入相同的值可以得到相同的结果。对系统的其它部分也不会产生副作用。

第一个Redux store

使用Redux.createStore()来创建一个store,并且将所有的reducers作为参数传递给它,此处以一个reducer为例子:

var userReducer = function(state=[], action) {
  if (action.type === 'ADD_USER') {
    var newState = state.concat([action.user]);
    return newState;
  }
  return state;}// 创建一个store,并且将reducer作为参数传递给它var store = Redux.createStore(userReducer);// 将action传递给store,告诉store我们有改变状态的意向store.dispatch({
  type: 'ADD_USER',
  user: {name: 'cpselvis'}});

上述代码运行后发生的事情:

  • Store被创建

  • reducer确定初始的state的值是空数组

  • action被转发给store

  • reducer将newUser添加到state中,并且将它返回,更新store

通过这段代码可以发现:reducer函数实际上被执行了2次,一次是store创建的时候,一次是action被转发之后。另外需要注意:Redux希望reducer函数总是返回一个新的状态。这时的结果:

store.getState();  // => [{name: 'cpselvis'}]

通过这个例子,可以总结出Redux的工作流程如图:
1030776-20170112135009463-1406392687.jpg

到这里,component -> action -> store -> reducer -> state 的单向数据流的问题就讲完了。概括的说就是:React组件里面获取了数据之后(比如ajax请求),然后创建一个action通知store我有这个想改变state的意图,然后reducers(一个action可能对应多个reducer,可以理解为action为订阅的主题,可能有多个订阅者)来处理这个意图并且返回新的state,接下来store会收集到所有的reducer的state,最后更新state。














本文转自xsster51CTO博客,原文链接:http://blog.51cto.com/12945177/1932209 ,如需转载请自行联系原作者







版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
10099 0
java 线程的几种状态
java thread的运行周期中, 有几种状态, 在 java.lang.Thread.State 中有详细定义和说明: NEW 状态是指线程刚创建, 尚未启动 RUNNABLE 状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一...
720 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
10887 0
[译] iOS 响应式编程:Swift 中的轻量级状态容器
本文讲的是[译] iOS 响应式编程:Swift 中的轻量级状态容器,MVC 模式(读作:臃肿的视图控制器),到那些 MV* 模式(比如 MVP,MVVM),再到听起来有点吓人的 Viper,那么我们该如何选择?
1005 0
Java线程的5种状态及切换(透彻讲解)
Java中的线程的生命周期大体可分为5种状态。 ①NEW:这种情况指的是,通过New关键字创建了Thread类(或其子类)的对象 ②RUNNABLE:这种情况指的是Thread类的对象调用了start()方法,这时的线程就等待时间片轮转到自己这,以便获得CPU;第二种情况是线程在处于RUNNABLE状态时并没有运行完自己的run方法,时间片用完之后回到RUNNABLE状态;还有种情况就是处于BLOCKED状态的线程结束了当前的BLOCKED状态之后重新回到RUNNABLE状态。
700 0
JavaScript状态栏显示滚动文字
   走马灯的效果!
863 0
+关注
10146
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载