作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Verma Sourabh的头像

By Verma Sourabh

苏拉布拥有广泛而通用的技能. 他在移动和后端开发方面的经验使他能够快速构建原型.

以前在

Silverpush
分享

在丰富而强大的网络和移动应用程序不断增长的生态系统中, 有越来越多的状态需要管理, 喜欢当前用户, 加载的项目列表, 加载状态, 错误, 还有更多. 回来的是解决这个问题的一种方法,它将状态保存在一个全局对象中.

回来的的一个限制是它不支持 异步行为 开箱即用. 对此的一个解决方案是 redux-可观测The它基于RxJS, RxJS是JavaScript中用于响应式编程的强大库. RxJS是反应iveX的一个实现, 一种用于响应式编程的API,起源于微软. 反应iveX结合了响应式范式的一些最强大的特性, 函数式编程,观察者模式和迭代器模式.

在本教程中,我们将学习回来的及其与反应的使用. 我们还将探讨 反应性编程 使用RxJS,以及它如何使繁琐和复杂的异步工作变得非常简单.

最后, 我们将学习redux-可观测The, 一个利用RxJS进行异步工作的库, 然后使用回来的和回来的 -可观测的在反应本地中构建一个应用程序.

回来的

正如它在GitHub上描述的那样,回来的是“JavaScript应用程序的可预测状态容器”.“它为JavaScript应用程序提供了一个全局状态, 保持状态和动作远离反应组件.

在没有回来的的典型反应应用程序中, 我们必须通过属性将数据from根节点传递给子节点, or 道具. 这种数据流对于小型应用程序来说是可管理的,但随着应用程序的增长,它会变得非常复杂. 回来的允许我们拥有彼此独立的组件, 因此,我们可以把它作为真理的单一来源.

回来的可以在反应中使用 react-redux, 它为反应组件提供绑定,以便from回来的读取数据并调度操作以更新回来的状态.

回来的

回来的可以被描述为三个简单的原则:

1. 真理的单一来源

状态 整个应用程序的所有数据都存储在一个对象中. 在回来的中,这个对象由 商店. 在任何回来的应用程序中都应该有一个单独的商店.


»控制台.日志(存储.getState ())
«{用户:{...}, todos: {...} }

要from反应组件中的回来的中读取数据,我们使用 连接 函数 react-redux. 连接 接受四个参数,它们都是可选的. 现在,我们将关注第一个,叫做 mapStateToProps.


/ * UserTile.js */

from'react-redux'导入{连接};

类UserTile扩展反应.组件{
    呈现(){
        return 

{ 这.道具.用户.name }

} } mapStateToProps(状态) { 返回{用户: 状态.用户} } 导出默认连接(mapStateToProps)(UserTile)

在上面的例子中, mapStateToProps 接收全局回来的状态作为其第一个参数,并返回一个对象,该对象将与传递给的道具Merge 它的父组件.

2. 状态为只读

回来的状态对于反应组件来说是只读的,改变状态的唯一方法是发出一个 行动. 动作是表示改变状态意图的普通对象. 每个动作对象都必须有 类型 字段,且值必须为字符串. 除此之外,操作的内容完全取决于你,但大多数应用程序都遵循 flux-st和ard-行动 为mat,它将一个动作的结构限制为只有四个键:

  1. 类型 操作的任何字符串标识符. 每个动作都必须有一个独特的动作.
  2. 有效载荷 任何操作的可选数据. 它可以是任何时间,并包含有关动作的信息.
  3. 错误 任何可选的布尔属性,如果操作表示错误,则设置为真正的. 这类似于被拒绝 承诺. 字符串 操作的标识符. 每个动作都必须有一个独特的动作. 按照惯例,当 错误 is 真正的, 有效载荷 应该是错误对象吗.
  4. meta 元可以是任何类型的值. 它适用于不属于有效负载的任何额外信息.

下面是两个行动的例子:


商店.调度({
    类型:“可以”,
    有效载荷:“21”,
});

商店.调度({
    类型:“GET_USER_SUCCESS”,
    有效载荷:{
        用户:{
            id: '21',
            名称:“Foo”
        }
    }
});

3. 使用纯函数改变状态

全局回来的状态是使用称为reducers的纯函数更改的. 减速器接受前一个状态和动作并返回下一个状态. reducer创建一个新的状态对象,而不是改变现有的状态对象. 根据应用的大小,回来的 商店可以有一个或多个reducer.


/ *存储.js */

from“redux”中导入{combineReducers, createStore}

函数用户(状态 = {}, 行动) {
  开关(行动.类型){
    例“GET_USER_SUCCESS”:
      返回操作.有效载荷.用户
    默认值:
      返回状态
  }
}

函数todos(状态 = [], 行动) {
  开关(行动.类型){
    例“ADD_TODO_SUCCESS”:
      返回(
        ...状态,
        {
          Id: uuid(), //随机uuid生成器函数
          文字:行动.文本,
          完成:假
        }
      ]
    例“COMPLETE_TODO_SUCCESS”:
      返回状态.map(todo => {
        如果(待办事项.Id === 行动.id) {
          返回{
            ...待办事项,
            完成:真
          }
        }
        返回待办事项
      })
    默认值:
      返回状态
  }
}
const rootReducer = combineReducers({用户, todos})
const 商店 = createStore(rootReducer)

与读取状态类似,我们可以使用a 连接 分派动作的函数.


/ * UserProfile.js */
类配置文件扩展反应.组件{
    h和leSave(用户){
        这.道具.updateUser(用户);
    }
}

mapDispatchToProps(调度){
    返回({
        updateUser: (用户) => 调度({
            类型:“GET_USER_SUCCESS”,
            用户,
        }),
    })
}

导出默认连接(mapStateToProps, mapDispatchToProps)(Profile);

RxJS

RxJS

反应性编程

响应式编程是一种声明式编程范式,它处理数据流。以及它的传播和变化. RxJS是一个用于JavaScript响应式编程的库,它有一个概念 可见,这是一个观察者可以看到的数据流 订阅 ,该观察者将随时间传递数据.

一个可观察对象的观察者是一个具有三个函数的对象: 下一个, 错误, 完整的. 所有这些函数都是可选的.


可观测的.订阅({
  下一个: value => console.log(' Value是${Value} '),
  错误: err => console.日志(err),
  完整的: () => console.日志('完成'),
})

.订阅 函数也可以有三个函数而不是一个对象.


可观测的.订阅(
  value => console.log(' Value是${Value} '),
  err => console.日志(err),
  () => console.日志(完成)
)

我们可以通过创建an的对象来创建一个新的可观察对象 可观测的,传入一个接收订阅者(即观察者)的函数. 订阅者有三个方法: 下一个, 错误, 完整的. 订阅者可以根据需要多次使用值调用下一个,并且 完整的 or 错误 最后. 后调用 完整的 or 错误,可观察对象不会将任何值推入流中.


fromrxjs中导入{Observable}
const 可观察到的美元 = new 可观测的 (function 订阅(订阅者)) {
  const intervalId = setInterval(() => {
    订阅者.下一个(“嗨”);
    订阅者.完成()
    clearInterval (intervalId);
  }, 1000);
});
可观察到的美元.订阅(
  value => console.log(' Value是${Value} '),
  err => console.日志(err)
)

上面的示例将打印 价值是高的 1000毫秒后.

每次手工创建一个可观察对象会变得冗长乏味. 因此,RxJS有很多函数来创建一个可观察对象. 一些最常用的是 of, from, ajax.

of

of 接受一个值序列并将其转换为流:


fromrxjs中导入{of}
of(1,2,3, 'Hello', 'World').订阅(value => console.日志(值))
// 1 2 3 Hello World

from

from 将几乎所有内容转换为值流:


fromrxjs中导入{from}


From ([1,2,3]).订阅(控制台.日志)
// 1 2 3


(新承诺.解决(“Hello World”)).订阅(控制台.日志)
// 'Hello World'


from(fibonacciGenerator).订阅(控制台.日志)
// 1 1 2 3 5 8 13 21 ...

ajax

ajax 接受字符串URL或创建一个可观察对象来发出HTTP请求. ajax 有一个功能 ajax.getJSON, 它只返回嵌套的响应对象fromAJAX调用没有返回的任何其他属性 ajax ():


from'rxjs/ajax'中导入{ajax}

ajax (http://jsonplaceholder.typicode.com/todos/1”).订阅(控制台.日志)
//{请求,响应:{用户Id, id, title, 完整的d}, responseType, status}


ajax.getJSON(“http://jsonplaceholder.typicode.com/todos/1”).订阅(控制台.日志)
// {用户Id, id, title, 完整的d}


Ajax ({url, 方法, headers, body}).订阅(控制台.日志)
// {...}

有很多方法可以创建一个可观察对象(你可以看到完整的列表) 在这里).

运营商

操作符是RxJS的一个真正的发电站, 它有一个操作员,几乎任何你需要的东西. fromRxJS 6开始, 操作符不是可观察对象上的方法,而是应用在可观察对象上的纯函数 .管 方法.

map

map 接受一个单参数函数,并对流中的每个元素应用投影:


fromrxjs中导入{of}
from“rxjs/operators”中导入{map}

of(1, 2, 3, 4, 5).管(
  map(i=> i * 2)
).订阅(控制台.日志)
// 2, 4, 6, 8, 10

Map

Filter

Filter 接受单个参数,并from流中删除给定函数返回false的值:


fromrxjs中导入{of}
from“rxjs/operators”中导入{map, Filter}

of(1, 2, 3, 4, 5).管(
  map(i => i * i),
  Filter(i => i % 2 === 0)
).订阅(控制台.日志)
// 4, 16

Filter

flatMap

flatMap Operator接受一个函数,该函数将蒸汽中的每个项映射到另一个流中,并将这些流的所有值扁平化:


fromrxjs中导入{of}
from'rxjs/ajax'中导入{ajax}
fromrxjs/operators中导入{flatMap}

of(1, 2, 3).管(
  flatMap(page => ajax.toJSON (http://example.com/blog?大小= 2&页面页面= ${}”)),
).订阅(控制台.日志)
//[{博客1},{博客2},{博客3},{博客4},{博客5},{博客6}]

FlatMap

Merge

Merge 按照到达的顺序Merge两个流中的项:


from'rxjs'中导入{interval, Merge}
fromrxjs/operators中导入{管, take, mapTo}

Merge(
  时间间隔(150).管(带(5),mapTo (' A ')),
  时间间隔(250).管(带(5),mapTo (B))
).订阅(控制台.日志)
// a b b a b b a b b b

Merge

操作符的完整列表是可用的 在这里.

回来的-Observable

回来的-Observable

按照设计,回来的中的所有操作都是同步的. 回来的-可观测的是回来的的中间件,它使用可观察流来执行异步工作,然后在回来的中调度另一个带有异步工作结果的操作.

回来的-可观测的是基于 史诗. 史诗是一个执行一系列动作的函数, 以及可选的状态流,并返回动作流.

函数(行动$: Observable<行动>, 状态$: StateObservable<状态>):可观察到的<行动>;

按照惯例,每个流变量(_aka _可观测的)以a结尾 $. 在我们使用redux-可观测The之前,我们必须把它作为中间件添加到我们的商店中. 因为史诗是由可观察对象组成的流,而每一个动作的退出都会被重新输送到流中, 返回相同的操作将导致无限循环.


const epic = 行动$ => 行动$.管(
    Filter(行动 => 行动.类型 === 'FOO'),
    mapTo({类型: 'BAR'}) //不改变返回的操作类型
                           //也会导致无限循环
)
// or
from'redux-可观测The'中导入{ofType}
const epic = 行动$ => 行动$.管(
    减低(“FOO”),
    mapTo({类型: BAZ'})
)

可以把这种响应式体系结构想象成一个管道系统,其中每个管道的输出反馈到每个管道, 包括自己, 还有回来的的还原器. 这些管道顶部的Filter决定了什么可以进入,什么被阻挡.

让我们看看乒乓史诗是如何运作的. 它接收一个ping,将其发送到服务器,并在请求完成后将一个pong发送回应用程序.


const pingEpic = 行动$ => 行动$.管(
    减低(“平”),
    flatMap(行动 => ajax('http://example.com/pinger ')),
    mapTo({类型: 'PONG'})
)

现在,我们将通过添加史诗和检索用户来更新原始的todo存储.

from“redux”中导入{combineReducers, createStore}
from“redux-可观测The”中导入{ofType, combineepic, createEpicMiddleware};
fromrxjs/operators中导入{map, flatMap}
from'rxjs/ajax'中导入{ajax}


// ...
/*如上定义的用户和任务减少器*/
const rootReducer = combineReducers({用户, todos}) 

createEpicMiddleware();
const 用户Epic = 行动$ => 行动$.管(
    减低('可以'),
    flatMap(() => ajax.getJSON(“http://foo.酒吧.com/get-用户 ')),
    map(用户 => ({ 类型:“GET_USER_SUCCESS”, 有效载荷: 用户}))
)

const addTodoEpic = 行动$ => 行动$.管(
    减低(“ADD_TODO”),
    flatMap(行动 => ajax({
        url: http://foo.酒吧.com/add-todo’,
        方法:“文章”,
        正文:{text:动作.有效载荷}
    })),
    map(data => data.反应),
    map(todo => ({ 类型: 'ADD_TODO_SUCCESS', 有效载荷: todo }))
)
const 完整的TodoEpic = 行动$ => 行动$.管(
    减低(“COMPLETE_TODO”),
    flatMap(行动 => ajax({
        url: http://foo.酒吧.com/完整的-todo’,
        方法:“文章”,
        主体:{id: 行动.有效载荷}
    })),
    map(data => data.反应),
    map(todo => ({ 类型: 'COMPLEE_TODO_SUCCESS', 有效载荷: todo }))
)

const rootEpic = combineepic (用户Epic, addTodoEpic, 完整的TodoEpic)
const 商店 = createStore(rootReducer, applyMiddleware(epicMiddleware))
epicMiddleware.运行(rootEpic);

重要:史诗就像RxJS中的任何其他可观察流一样. 它们可能以完整状态或错误状态结束. 在此状态之后,史诗和您的应用程序将停止工作. 所以你必须抓住蒸汽中每一个潜在的错误. 你可以使用 __catchError__ 这个操作符. 更多信息: redux-可观测The中的错误处理.

一个响应式待办事项应用程序

添加一些UI后,一个(最小的)演示应用看起来像这样:

一个响应式待办事项应用程序

反应, 回来的和RxJS教程总结

我们了解了什么是响应式应用. 我们还了解到 回来的、RxJS和redux-可观测The,甚至用反应本地在Expo上创建了一个响应式Todo应用. 为 反应反应本地 开发人员,目前的趋势提供了一些非常强大的 状态管理选项.

再一次,这个应用程序的源代码是打开的 GitHub. 欢迎在下面的评论中分享你对响应式应用状态管理的看法.

了解基本知识

  • 回来的是什么?

    回来的是JavaScript应用程序的一个可预测的全局状态容器. 它将状态管理和变更传播逻辑from应用程序的反应组件中抽象出来.

  • 什么是响应式编程?

    响应式编程是一种声明式编程范式,它将数据作为流处理,并通过该流传播状态变化.

  • 什么是反应iveX?

    反应iveX是一个用于可观察流异步编程的API. 它结合了来自观察者模式的最佳想法, 迭代器模式, 函数式编程.

  • 什么是RxJS?

    RxJS是反应iveX API在JavaScript中的实现库. 它利用了JavaScript的动态特性以及广泛的反应iveX API.

  • 谁创造了反应iveX和RxJS?

    微软的计算机科学家Erik Meijer创建了反应iveX API及其实现 .网。. RxJS是作为Rx的一个移植而创建的.网。由不同的工程师.

  • RxJS值得学习吗?

    RxJS为用函数式编程处理异步任务提供了大量API, 这有助于使用重试之类的操作, 节流, 指数回退, 缓存, 错误处理, 和更多的.

  • 什么是redux-可观测The?

    回来的 -可观测的是一个在回来的中处理异步任务的库. 它的工作原理是from回来的捕获一个已调度的操作,并对其执行一些异步工作. 工作完成后, redux-可观测The 通过调度另一个操作将响应添加到回来的状态容器中.

  • 我们能在没有反应和回来的的情况下使用RxJS吗?

    RxJS是反应iveX的实现,可以与任何状态管理或UI库一起使用, 或者甚至是普通的JavaScript. 它提供了一个强大的API来管理可观察流形式的异步工作. 这些流可以连接到任何其他库或直接使用. 在JavaScript之外,您可以在反应iveX上找到针对常见语言的反应iveX实现.io.

聘请Toptal这方面的专家.
现在雇佣
Verma Sourabh的头像
Verma Sourabh

位于 古尔冈,哈里亚纳邦,印度

成员自 2019年8月26日

作者简介

苏拉布拥有广泛而通用的技能. 他在移动和后端开发方面的经验使他能够快速构建原型.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

以前在

Silverpush

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® 社区.