RxJS和React的多个点击元素形成单个数据数组
RxJS and React multiple clicked elements to form single data array
所以我刚开始尝试学习rxjs,并决定我将在我目前正在使用React的UI上实现它(我有时间这样做,所以我去了)。然而,我仍然很难理解它到底是如何工作的……不仅是"基本"的东西,比如什么时候实际使用一个Subject,什么时候使用一个Observable,或者什么时候只使用React的本地状态,还有如何链接方法等等。这太宽泛了,所以我有一个具体的问题。
假设我有一个UI,其中有一个过滤器(按钮)列表,它们都是可点击的。每当我点击其中一个时,我首先要确保随后的操作将会失效(为了避免过快和过于频繁地发出网络请求),然后我要确保如果它被点击(活动),它将被推入数组,如果它再次被点击,它将离开数组。现在,这个数组最终应该包括当前单击或选择的所有按钮(过滤器)。
然后,当debounce时间完成时,我希望能够使用该数组并通过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>
);
}
}
在没有RxJS的情况下,我可以很容易地做到这一点,只是自己检查数组并使用一个小debounce之类的东西,但我选择这样做是因为我实际上想尝试理解它,然后能够在更大的场景中使用它。然而,我必须承认我不知道最好的方法是什么。有这么多的方法和不同的东西涉及到这(包括模式和库),我只是有点困在这里。
无论如何,欢迎任何帮助(以及关于如何改进此代码的一般评论)。提前感谢!
--------------------------------- ---------------------------------
我已经在我的代码中实现了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>
);
}
}
再次感谢大家!
使用Rx.Observable.fromevent
(参见https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/events.md#converting-a-dom-event-to-a-rxjs-observable-sequence)使你的过滤器项成为点击事件的Observable流——它理解一个多元素选择器来处理点击。
你想保持接收点击事件,直到一个debounce已经被击中(用户已经启用/禁用了她想要使用的所有过滤器)。您可以将Buffer
操作符用于此操作,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);
(https://jsfiddle.net/crunchie84/n1x06016/6/)
实际上,你的问题是关于RxJS,而不是React本身。所以这很简单。假设您有两个函数:
const removeTag = tagName =>
tags => {
const index = tags.indexOf(index)
if (index !== -1)
return tags
else
return tags.splice(index, 1, 0)
}
const addTag = tagName =>
tags => {
const index = tags.indexOf(index)
if (index !== -1)
return tags.push(tagName)
else
return tags
}
你可以使用scan
:
const modifyTags$ = new Subject()
modifyTags$.pipe(
scan((tags, action) => action(tags), [])
).subscribe(tags => sendRequest(tags))
modifyTags$.next(addTag('a'))
modifyTags$.next(addTag('b'))
modifyTags$.next(removeTag('a'))
或者为tags
设置一个单独的对象:
const tags$ = new BehaviorSubject([])
const modifyTags$ = new Subject()
tags$.pipe(
switchMap(
tags => modifyTags$.pipe(
map(action => action(tags))
)
)
).subscribe(tags$)
tags$.subscribe(tags => sendRequest(tags))
- 使用jquery将单个换行符替换为span元素
- 如何创建具有单个元素或元素数组的数组
- 扩展验证.js并验证单个元素
- <noscript>标记单个元素或类似的东西
- Angularjs元素在一个调用中同时查找select和input-这是单个标记查找的极限
- 单个功能的多个元素
- 如何使用jQuery跟踪单个dom元素上的点击事件,并相应地更新其点击计数器
- 用程序从单个HTML元素中删除CSS样式规则
- 在javascript中阻止文档执行默认操作时,在单个元素中使用默认事件
- 从单个元素中Jquery关闭事件
- Javascript通过元素,替换表的单个部分
- 将复杂的html/css/js元素添加到单个Wordpress页面中
- 使用D3.js从单个数据点生成多个DOM元素
- 为什么 .data() 适用于一组元素,但不适用于单个元素
- 访问单个 Json 对象中的元素
- 将 CSS 类添加到数组中的单个元素
- 如何将动态追加元素的多个值存储到单个 var 中,然后检索到函数中
- 在 Ext JS 4 上显示/隐藏系列的单个行元素
- 从单个 Div 元素中删除单击事件
- 更改 JS 数组中的单个对象会更改所有元素