RxJS和React多个单击元素以形成单个数据数组

dea*_*xes 8 javascript arrays rxjs reactjs

所以我刚开始尝试学习rxjs并决定在我正在使用React工作的UI上实现它(我有时间这样做,所以我去了).然而,我仍然很难绕过它的实际工作方式......不仅仅是"基本"的东西,比如何时实际使用Subject以及何时使用Observable,或者何时只使用React的本地状态,还要如何连锁方法等.虽然这太宽泛了,所以这就是我遇到的具体问题.

假设我有一个UI,其中有一个过滤器(按钮)列表都是可以解决的.任何时候我点击其中一个我想要,首先,确保后面的操作将去抖(以避免太快和太频繁地发出网络请求),然后我想确保如果它被点击(它将被推入一个数组,如果它再次被点击,它将离开数组.现在,此数组最终应包含当前单击或选中的所有按钮(过滤器).

然后,当去抖动时间结束时,我希望能够使用该数组并通过Ajax将其发送到我的服务器并用它做一些事情.

import React, { Component } from 'react';
import * as Rx from 'rx';

export default class CategoryFilter extends Component {
 constructor(props) {
    super(props);

    this.state = {
        arr: []
    }

    this.click = new Rx.Subject();
    this.click
    .debounce(1000)
    // .do(x => this.setState({
    //  arr: this.state.arr.push(x)
    // }))
    .subscribe(
       click => this.search(click),
       e => console.log(`error ---> ${e}`),
       () => console.log('completed')
    );
 }

search(id) {
    console.log('search --> ', id);
    // this.props.onSearch({ search });
}

clickHandler(e) {
    this.click.onNext(e.target.dataset.id);
}

render() {
    return (
        <section>
            <ul>
                {this.props.categoriesChildren.map(category => {
                    return (
                        <li
                            key={category._id}
                            data-id={category._id}
                            onClick={this.clickHandler.bind(this)}
                        >
                            {category.nombre}
                        </li>
                    );
                })}
            </ul>
        </section>
    );
 }
}
Run Code Online (Sandbox Code Playgroud)

我可以轻松地在没有RxJS的情况下进行此操作,只是自己检查阵列并使用一个小的去抖,什么不是,但我选择这样做因为我实际上想要尝试理解它然后能够在更大的场景中使用它.但是,我必须承认我对最好的方法感到迷茫.这有很多方法和不同的东西(模式和库),我只是被困在这里.

无论如何,任何和所有帮助(以及关于如何改进此代码的一般性评论)都是受欢迎的.提前致谢!

--------------------------------- 更新 ---------------- -----------------

我已经在我的代码中实现了Mark的一部分建议,但这仍然存在两个问题:

1-我仍然不确定如何过滤结果,以便数组只保存单击(和激活)按钮的ID.换句话说,这些将是行动:

  • 单击一个按钮 - >将其ID转入阵列
  • 再次单击相同的按钮(可能是在第一次单击后或在任何其他时间) - >从阵列中删除其ID.

这必须工作才能通过ajax实际发送带有正确过滤器的数组.现在,我甚至不确定这是一个可能的RxJS操作,但人们可以梦想...(另外,我愿意打赌它是).

2-也许这是一个更大的问题:当我在这个视图上时,我怎样才能真正维护这个数组.我猜我可以使用React的本地状态,只是不知道如何使用RxJS.因为当前是,缓冲区仅返回在去抖时间结束之前已经单击的按钮,这意味着它每次都"创建"一个新数组.这显然不是正确的行为.它应始终指向现有的数组和过滤器并使用它.

这是当前的代码:

import React, { Component } from 'react';
import * as Rx from 'rx';

export default class CategoryFilter extends Component {
 constructor(props) {
    super(props);

    this.state = {
        arr: []
    }

    this.click = new Rx.Subject();
    this.click
    .buffer(this.click.debounce(2000))
    .subscribe(
        click => console.log('click', click),
        e => console.log(`error ---> ${e}`),
        () => console.log('completed')
    );
 }

search(id) {
    console.log('search --> ', id);
    // this.props.onSearch({ search });
}

clickHandler(e) {
    this.click.onNext(e.target.dataset.id);
}

render() {
    return (
        <section>
            <ul>
                {this.props.categoriesChildren.map(category => {
                    return (
                        <li
                            key={category._id}
                            data-id={category._id}
                            onClick={this.clickHandler.bind(this)}
                        >
                            {category.nombre}
                        </li>
                    );
                })}
            </ul>
        </section>
    );
 }
}
Run Code Online (Sandbox Code Playgroud)

谢谢,再一次!

Mar*_*ten 1

使用以下方法使您的过滤器项目成为可观察的点击事件流Rx.Observable.fromevent(请参阅https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/events.md#converting-a-dom-event-to-a -rxjs-observable-sequence ) - 它理解用于点击处理的多元素选择器。

您希望继续接收点击事件,直到发生去抖(用户已启用/禁用她想要使用的所有过滤器)。您可以将Buffer 运算符与 a 一起使用closingSelector,当关闭缓冲区并发出缓冲的值时需要发出一个值。

但留下了如何知道当前实际状态的问题。

更新

.scan使用运算符创建 filterState 数组并对它们进行反跳似乎要容易得多。

const sources = document.querySelectorAll('input[type=checkbox]');
const clicksStream = Rx.Observable.fromEvent(sources, 'click')
  .map(evt => ({
        name:  evt.target.name,
        enabled: evt.target.checked
  }));

const filterStatesStream = clicksStream.scan((acc, curr) => {
  acc[curr.name] = curr.enabled;
  return acc
}, {})
.debounce(5 * 1000)

filterStatesStream.subscribe(currentFilterState => console.log('time to do something with the current filter state: ', currentFilterState);
Run Code Online (Sandbox Code Playgroud)

https://jsfiddle.net/crunchie84/n1x06016/6/