升级React:Redux

  1. Redux is “Flux-like”(译:Redux是”像Flux的“)
  2. 我们的第一个Redux Store

本文翻译于原文链接

Redux是一个在javascript应用中同时管理数据和UI视图状态的工具。它被认为是完美管理单页应用复杂状态的杰作。同时它不是一定要用于一个特定的框架上(比如React),虽然它的作者心中是想解决React的复杂状态管理,但是它依然可以用在Angular,甚至jQuery应用之上。

告诉大家更多的是,它是经过一项“time travel”的实验而想出来的——这是它被开发出来的真实的原因,我们将在后面讨论。

就像我们之前的教程中看到的一样,React在组件间流动数据,更具体的说,这被叫做单向数据流——数据流向是从父到子一个方向的。对于这个单向数据流特征,React没有明显表示如何处理两个没有父子关系的组件的通信:

很low的方式来处理没有父子关系的组件通信

React不推荐直接像上图那样组件与组件间通信。即使它特性支持上面的通信,也不要这样做。因为这种实践被认为是不好,极易导致代码写得像面条一样混乱。

React确实提供了一种解决上述问题的方案,但是它并没有在框架层面实现,而是需要开发者,也就是你,自己去实现它。下面是一段React文档的原文:

For communication between two components that don’t have a parent-child relationship, you can set up your own global event system. … Flux pattern is one of the possible ways to arrange this.

翻译为:对于无父子关系的两个组件间的通信,你可以搭建你自己的全局事件系统。…Flux模式是可能处理这个问题的多种方式之一。

这就是Redux出来的原因。Redux提供了一种方案把你所有的应用状态保存在一个地方,叫做”store“。然后组件把状态的变化通知到store,同时组件订阅store,就可以察觉到状态的变化,从而不直接在组件间通信。

dispatch and subscribe

store在整个应用中可以看做所有状态变化的中间人。引入Redux,组件之间不直接交流,而是遵循”single source of truth“(译:单真理之源),也就是store这个真理之源。这和其他方式中,应用的各部分直接互相交流有很大不同。有时,那些方式被认为是错误的或者令人混淆的:

without redux or with redux

引入redux,很清晰地知道组件获取它们的状态通过唯一的store。同时也清晰地知道,组件在何处通知状态的改变,也是store。一个组件的通知状态改变只聚焦在dispatch(译:派遣)一个改变到store上,而不需要关心其他的组件是否需要这个状态的变化。所以Redux使数据流容易去推测。

使用store去协调一个应用的状态实际上是一种模式,也就是上文提到的Flux 模式。它是一个设计模式赞扬了像React那样的单向数据流的工程。Redux与Flux相似,但是它们怎样相似呢?

Redux is “Flux-like”(译:Redux是”像Flux的“)

Flux是一种模式,而不是像Redux是个工具,因此不是一个你可以下载的东西。尽管Redux是一个在其它东西像Elm,被Flux影响的工具。有很多指导文章在那里去比较Redux和Flux。它们大多数总结Redux是Flux或者像Flux,取决于一个人定义Flux规则有多严格。最终,这都不重要。Facebook非常喜欢和支持Redux,以至于最后聘请了Redux的核心开发者Dan Abramov。

这篇文章假设你一点也不熟悉Flux模式。但是如果你很熟悉,你会发现一些小的不同,特别是考虑Redux的三大指导法则:

  1. Single source of truth(译者注:单真理之源)

    Redux对于整个应用的状态仅仅使用一个store。因为所有的状态放在一个地方,Redux把这个叫做Single source of truth。

    store的数据结构最终取决于你,但是它对于一个真实的应用,是一个典型的深度嵌套的对象。

    Redux一个store的方式是与多个store的Flux众多的不同之一。

  2. 状态(state)是只读的

    根据Redux文档,”改变状态的唯一方式是发出一个action,一个描述发生了什么的对象。“

    这意味着这个应用不能直接改变状态,而是通过action来表达一种去改变store里面的状态的意图。

    store对象本身只有一个非常小的API对应4个方法:

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

    因此你可以看到,没有一个方法是设置状态的。因此,对于一个应用来说,发出一个action是唯一可以反映一个状态变化的方式:

    1
    2
    3
    4
    5
    6
    7
    var action = {
    type: 'ADD_USER',
    user: {name: 'Dan'}
    };

    // 假设一个store对象已经被创建了
    store.dispatch(action);

    dispatch()方法发送一个对象到Redux,这个对象被我们称为action。这个action被描述为一个”payload(译:载荷,承载着状态修改时要用到的信息)“持有一个type和其他修改状态时要用到的数据——这个例子里面是一个user对象。记住,除了type这个属性,其他的载荷数据就看你如何去设计,不是固定的,比如这里使用一个user对象,用在reducer中。

  3. 修改由纯函数组成

    就像刚才描述的那样,Redux不允许应用直接修改状态,而是用传递的action表明状态的变化,并意图去修改状态。Reducers是一些函数可以接收发送过来的action,并真正的去操作状态的修改。

    一个reducer接收当前的状态作为一个参数,并能够通过构造出一个新状态来修改原来状态:

    1
    2
    3
    4
    5
    // Reducer 函数
    var someReducer = function(state, action) {
    ...
    return state;
    }

    Reducer应该使用纯函数来编写。纯函数描述了一个函数具有下述四个特点:

    • 它不调用外部网络和数据库
    • 它的返回值仅仅依赖于它的参数
    • 它的参数应该是不能改变的
    • 用同样的实参调用一个纯函数将总是返回相同的结果

    这些被称作”纯“,因为它什么也不做但是返回的值基于它的参数。它对系统其它部分没有任何副作用。

我们的第一个Redux Store

首先,用Redux.createStore()创建一个store并且传递所有的reducer作为它的参数。让我们看看一个仅有一个reducer的简单例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 注意使用.push()不是一个最好的方式。这里只是为了简单的示例。
// 我们将在后面的章节讲述为什么

// reducer函数
var userReducer = function(state, action) {
if (state === undefined) {
state = [];
}
if (action.type === 'ADD_USER') {
state.push(action.user);
}
return state;
}

// 通过传入一个reducer创建一个store
var store = Redux.createStore(userReducer);

// 传递我们第一个action来传达一个改变状态的意图
store.dispatch({
type: 'ADD_USER',
user: {name: 'Dan'}
})

简单的总结一下上面发生了什么:

  1. store通过一个reducer创建;
  2. reducer创建了这个应用的初始状态为一个空的数组;
  3. store.dispatch()用一个新的user对象派遣了action
  4. reducer将新的user对象加入到状态里并且返回了它,这个返回的过程更新了store

reducer实际上被调用了两次在这个例子里面——一次是当store创建的时候,而另一次则是派遣action的时候。

当store被创建,Redux立即调用了reducers并且使用了它们返回的值作为初始的状态。第一次调用reducer时state是undefined。reducer的代码预期到这种情况,于是返回了一个空的数组来作用store的初始状态。

Reducers也在actions被派遣的时候被调用。因为从reducer返回的状态总是变成一个store的新的状态,所以Redux总是期望reducers返回一个状态。

在这个例子中,对我们reducer的第二个调用来自于我们的对action的派遣(译者注:store.dispatch())。记住,一个派遣的action描述了修改状态的意图,并且经常每次都承载着数据用于新状态。我们这次,Redux传递一个当前的状态(译者注:state)和一个action对象给reducer函数。这个action对象,现在有一个type属性叫做”ADD_USER“,允许了这个reducer来知道如何去改变状态。

(未完待续)


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 jaytp@qq.com

×

喜欢就点赞,疼爱就打赏