资讯专栏INFORMATION COLUMN

用RxJS和react开发mac地址输入框

CastlePeaK / 2442人阅读

摘要:项目简介本次使用了和开发了一个地址输入框,主要实现的功能有限制输入符合条件的字符并每隔两位可以自动添加用于分割的冒号。项目屏蔽了的事件处理,同时使用来手动控制光标。继承于和因此同时具有和两者的方法。后面的和都是需要利用最新的来进行判断的。

项目简介

本次使用了RxJS和react开发了一个mac地址输入框,主要实现的功能有限制输入符合条件的字符1-9,a-f,并每隔两位可以自动添加用于分割的冒号。项目屏蔽了react的事件处理,同时使用setSelectionRange来手动控制光标。可以查看项目的demo,项目地址

RxJS简介

RxJS 是 Reactive Extensions 在 JavaScript 上的实现,具体来说是一系列工具库,包括事件处理,函数节流,延时等函数,RxJS应用了’流‘的思想,同时具有事件和时间的概念。RxJS也可以用于处理异步流程,比起Promise具有可取消和可延迟,重试等优点。Promise vs Observable
RxJS中有两个比较重要的概念,分别是Observable和observer。Observable可以使用create,of,from,fromEvent等方法来产生流,而Observer可以对流进行观察。最后两者通过subscribe来结合,例子如下:

</>复制代码

  1. var Observable = Rx.Observable.create(observer => {
  2. observer.next(2);
  3. observer.complete();
  4. return () => console.log("disposed");
  5. });
  6. var Observer = Rx.Observer.create(
  7. x => console.log("Next:", x),
  8. err => console.log("Error:", err),
  9. () => console.log("Completed")
  10. );
  11. var subscription = Observable.subscribe(Observer);

来自构建流式应用—RxJS详解

更多关于RxJS,可以阅读Introduction | RxJS - Javascript library for functional reactive programming.

项目结构

</>复制代码

  1. // 监听事件,发起流和处理流
  2. componentDidMount () {
  3. this.t = ReactDOM.findDOMNode(this.refs.t)
  4. let keydownValue = Rx.Observable.fromEvent(this.t,"keydown").map(e => e.key.toUpperCase())
  5. this.sa = keydownValue.filter(value => value.length === 1 && value.match(/[0-9A-F]/)).subscribe(value => {this.setColon("before");this.insertValue(value); this.setColon();this.setDomValue()})
  6. // 省略类似的部分
  7. }
  8. // 取消订阅
  9. componentWillUnmount()
  10. this.sa.dispose()
  11. // 类似的部分省略
  12. }
  13. // 一些用到的方法,这里省略
  14. // 取消原生的事件监听
  15. render() {
  16. return (
  17. e.preventDefault()} ref="t"/>
  18. );
  19. }
项目详解

首先使用Rx.Observable.fromEvent来监听输入框的按键事件,并获取按键的key值,保存为keydownValue

</>复制代码

  1. let keydownValue = Rx.Observable.fromEvent(this.t,"keydown")
  2. .map(e => e.key.toUpperCase())

接着首先考虑输入字符的情况,在这里,显示筛选出按键符合要求的情况,接着在subscribe中对数据进行处理。在插入新的字符之前和之后,都需要判断是否在前面加上冒号,最后使用setDomValue来让保存在state中的value显示到输入框上。

</>复制代码

  1. this.sa = keydownValue
  2. .filter(value => value.length === 1 && value.match(/[0-9A-F]/))
  3. .subscribe(value => {
  4. this.setColon("before");
  5. this.insertValue(value);
  6. this.setColon();
  7. this.setDomValue()
  8. })

判断是否需要插入冒号的函数setColon,需要排除前面没有字符和周围已经有冒号的情况。

</>复制代码

  1. setColon = type => this.state.value.length &&
  2. (type !== "before" ? !this.isNearColon() : !this.isLastColon()) &&
  3. !(this.state.value.slice(0, this.state.pos).replace(/:/g, "").length%2) &&
  4. this.insertValue(":")

插入新字符的函数。在记录的光标位置pos值上插入新的字符,然后改变光标位置。如果在字符末尾有未完成的字符对(即1f:的形式)又在中间插入新的字符串且字符对已经到达六个,则删掉最后一个字符对。

</>复制代码

  1. insertValue = value => {
  2. if (this.state.value.length !== 17) {
  3. this.setState({
  4. ...this.state,
  5. value: this.state.value.slice(0, this.state.pos) +
  6. value + this.state.value.slice(this.state.pos, this.state.value.length)
  7. })
  8. this.setPos(this.state.pos + 1)
  9. if (this.state.value.split(":").length === 7) {
  10. this.setState({
  11. ...this.state,
  12. value: this.state.value.slice(0, this.state.value.lastIndexOf(":"))
  13. })
  14. }
  15. }}

接着是讲解关于删除的流,筛选按键值为"BACKSPACE"的流,执行deleteValue方法和setDomValue

</>复制代码

  1. this.sb = keydownValue.filter(value => value === "BACKSPACE")
  2. .subscribe(() => {
  3. this.deleteValue()
  4. this.setDomValue()
  5. })

deleteValue,在value和位置都大于零时才执行,如果删除后字符后,新的最后一个字符是冒号,则自动删掉该冒号。

</>复制代码

  1. deleteValue = () => {
  2. if (this.state.value.length && this.state.pos) {
  3. this.setState({
  4. ...this.state,
  5. value: this.state.value.slice(0, this.state.pos - 1) +
  6. this.state.value.slice(this.state.pos, this.state.value.length)
  7. })
  8. this.setPos(this.state.pos - 1)
  9. if (this.isLastColon()) {
  10. this.deleteValue()
  11. }
  12. }
  13. }

接着是订阅了左右方向键移动的流,比较简单,就不详细解释了。

</>复制代码

  1. this.sc = keydownValue
  2. .filter(value => value === "ARROWLEFT")
  3. .subscribe(() => this.moveLeft())
  4. this.sd = keydownValue
  5. .filter(value => value === "ARROWRIGHT")
  6. .subscribe(() => this.moveRight())
  7. moveLeft = () => this.state.pos > 0 &&
  8. this.setState({...this.state, pos: this.state.pos - 1})
  9. moveRight = () => this.state.pos !== this.state.value.length &&
  10. this.setState({...this.state, pos: this.state.pos + 1})

最后是让光标跳到pos的处理,setSelectionRange本用于文字的选择,但如果前两个参数为一样的数值,可以达到让光标跳到指定位置的效果。

</>复制代码

  1. this.se = keydownValue.subscribe(() => this.goPos())
  2. goPos = () => this.t.setSelectionRange(this.state.pos, this.state.pos)
170624更新

原本的模式跟react关系较少,因此修改调整了一下,主要的变化是启用了Subject,setStateAsync,在这里先介绍一下。

Rx.Subject

Subject继承于Obserable和Observer,因此同时具有Obserable和Observer两者的方法。通过来自于Observable的multicast方法可以挂载subject,并得到拥有相同执行环境的多路的新的Observable,关于他的订阅实际上是挂载在subject上。最后需要手动connect。 RxJS 核心概念之Subject,30 天精通 RxJS(24): Observable operators - multicast, refCount, publish, share

</>复制代码

  1. var source = Rx.Observable.from([1, 2, 3]);
  2. var multicasted = source.multicast(new Rx.Subject())
  3. // 通过`subject.subscribe({...})`订阅Subject的Observer:
  4. multicasted.subscribe({
  5. next: (v) => console.log("observerA: " + v)
  6. });
  7. multicasted.subscribe({
  8. next: (v) => console.log("observerB: " + v)
  9. });
  10. // 让Subject从数据源订阅开始生效:
  11. multicasted.connect();

其实可以用refCount来避免connect,用publish来代替 multicast(new Rx.Subject()),最后用share代替publish 和 refCount,因此代码可以写成

</>复制代码

  1. var multicasted = source.share()
setStateAsync

组件改为受控组件之后,setState中的异步特性展示了出来,setState后的下一步获取setState并不是最新的state,影响了程序的正常使用。
例如之前的新增函数的订阅。后面的inserValue和setColon都是需要利用最新的state来进行判断的。

</>复制代码

  1. this.sa = keydownValue
  2. .filter(value => value.length === 1 && value.match(/[0-9A-F]/))
  3. .subscribe(value => {
  4. this.setColon("before");
  5. this.insertValue(value);
  6. this.setColon();
  7. this.setDomValue()
  8. })

可以在setState的第二个参数中传入回调函数来解决这个问题,于是函数变成了这样,一层又一层的回调,十分不美观

</>复制代码

  1. this.sa = keydownValue
  2. .filter(value => value.length === 1 && value.match(/[0-9A-F]/))
  3. .subscribe(value => {
  4. this.setColon("before", () => {
  5. this.insertValue(value, () => {
  6. this.setColon()
  7. })
  8. })
  9. })

接着在网上找到了setStateAsync的函数,原理就是将setState转换成promise的形式,接着就能愉快的使用async await的语法来修改state了。React中setState同步更新策略

</>复制代码

  1. setStateAsync = state => new Promise(resolve => this.setState(state,resolve))
实际的调整

在componentDidMount中把keydownValue设置为同时具有Observable和Observe的方法的Subject,他一方面可以使用Observer的onNext方法来添加新的数据,另一方面可以继续使用Observable的操作符来对数据进行处理。

</>复制代码

  1. this.keydownValue = new Rx.Subject()
  2. let multicasted = this.keydownValue.map(e => e.key.toUpperCase()).share()
  3. this.sa = multicasted
  4. .filter(value => value.length === 1 && value.match(/[0-9A-F]/))
  5. .subscribe(async value => {
  6. await this.setColon("before")
  7. await this.insertValue(value)
  8. await this.setColon()
  9. this.goPos()
  10. })
  11. //下略

组件的render函数修改为

</>复制代码

handleE函数继续禁止默认事件,调用了新设置的Subject(keydownValue)的onNext方法,可以使得绑定在keydownValue上的订阅获得数据

</>复制代码

  1. handleE = e => {e.preventDefault();this.keydownValue.onNext(e)}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94223.html

相关文章

  • Rxjs 响应式编程-第六章 使Cycle.js的响应式Web应程序

    摘要:我们将使用,这是一个现代,简单,漂亮的框架,在内部使用并将响应式编程概念应用于前端编程。驱动程序采用从我们的应用程序发出数据的,它们返回另一个导致副作用的。我们将使用来呈现我们的应用程序。仅采用长度超过两个字符的文本。 Rxjs 响应式编程-第一章:响应式Rxjs 响应式编程-第二章:序列的深入研究Rxjs 响应式编程-第三章: 构建并发程序Rxjs 响应式编程-第四章 构建完整的We...

    EastWoodYang 评论0 收藏0
  • 大厂前端面试考什么?

    摘要:面试流程如果你时间紧张或是在职,大部分大公司是支持第一轮电话面试的,而腾讯,阿里的面试基本前两轮都是电话面。组员面总体来说考察的东西较基础。也有会问你设计模式,各种继承方法的对于大厂的面试来说,重要性可能比还要高。 背景 我最近一个月面试了20来个公司,其中有你们应该都听过的bat,今日头条,京东,网易,大疆,oppo,还有shopee,招商金科,有赞等比较不错的公司,收获了六个off...

    jubincn 评论0 收藏0
  • 使 AVA 做自动化测试

    摘要:单元测试,测试一个简单的组件。接口测试,用户信息接口测试。学习借鉴,一些使用做测试的开源项目。这里使用到的内置断言断言结果值等于我们想要的预期值,则测试通过。在里放入一个函数,函数自动执行,里面执行的结果必须抛出错误,则测试通过。 目录 1、为什么选择 AVA ?2、API 概览。3、准备工作。4、单元测试,测试一个简单的工具函数。5、使用 Promise、Async/await、Ob...

    Cruise_Chan 评论0 收藏0
  • FCC 成都社区·前端周刊 第 11 期

    摘要:正式发布已正式发布,新版本重点关注工具链以及工具链在中的运行速度问题。文章内容包括什么是内存,内存生命周期,中的内存分配,内存释放,垃圾收集,种常见的内存泄漏以及如何处理内存泄漏的技巧。 1. Angular 6 正式发布 Angular 6.0.0 已正式发布,新版本重点关注工具链以及工具链在 Angular 中的运行速度问题。Angular v6 是统一整体框架、Material ...

    lentrue 评论0 收藏0
  • FCC 成都社区·前端周刊 第 11 期

    摘要:正式发布已正式发布,新版本重点关注工具链以及工具链在中的运行速度问题。文章内容包括什么是内存,内存生命周期,中的内存分配,内存释放,垃圾收集,种常见的内存泄漏以及如何处理内存泄漏的技巧。 1. Angular 6 正式发布 Angular 6.0.0 已正式发布,新版本重点关注工具链以及工具链在 Angular 中的运行速度问题。Angular v6 是统一整体框架、Material ...

    NusterCache 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<