简体   繁体   English

如何将React状态绑定到RxJS可观察流?

[英]How to bind React state to RxJS observable stream?

can someone help me how to bind React State to RxJS Observable? 有人可以帮助我如何将React State绑定到RxJS Observable? I did sth like 我做了......

componentDidMount() {
  let source = Rx.Observable.of(this.state.val)
}

The ideal result is, whenever this.state.val updated (via this.setState(...) ) source get updated too, so I can combine source with other RxJS observable stream. 理想的结果是,每当this.state.val更新(通过this.setState(...)source得到更新,所以我可以将source与其他RxJS可观察流组合。

However, in this case, source only get updated once, even after this.state.val is updated and component is re-rendered. 但是,在这种情况下,即使在更新this.state.val并重新呈现组件之后, source只会更新一次。

// Ideal result:
this.state.val = 1
source.subscribe(val => console.log(x)) //=> 1
this.state.val = 2
source.subscribe(val => console.log(val)) //=> 2

// Real result:
this.state.val = 1
source.subscribe(val => console.log(x)) //=> 1
this.state.val = 2
source.subscribe(val => console.log(val)) //=> 1 ???WTH

It might be because componentDidMount() only invoked once in React lifetime. 这可能是因为componentDidMount()仅在React生命周期中调用过一次。 so I move source to componentDidUpdate() which is invoked everytime after component is rendered. 所以我将source移动到componentDidUpdate() ,每次渲染组件后都会调用它。 However, the result still remain the same. 但是,结果仍然保持不变。

So the question is how to make source updated whenever this.state.val updated? 所以问题是如果更新this.state.valthis.state.val更新source

Updated: Here is the solution I used to solve the prob, using Rx.Subject 更新:这是我用来解决概率的解决方案,使用Rx.Subject

// Component file
constructor() {
  super(props)
  this.source = new Rx.Subject()
_onChangeHandler(e) {
 this.source.onNext(e.target.value)
}
componentDidMount() {
  this.source.subscribe(x => console.log(x)) // x is updated
}
render() {
  <input type='text' onChange={this._onChangeHandler} />
}
// 

Update 更新

To abstract out some of the below complexity, use recompose's mapPropsStream or componentFromStream . 要抽象出下面的一些复杂性,请使用recompose的mapPropsStreamcomponentFromStream Eg 例如

const WithMouseMove = mapPropsStream((props$) => {
  const { handler: mouseMove, stream: mouseMove$ } = createEventHandler();

  const mousePosition$ = mouseMove$
    .startWith({ x: 0, y: 0 })
    .throttleTime(200)
    .map(e => ({ x: e.clientX, y: e.clientY }));

  return props$
    .map(props => ({ ...props, mouseMove }))
    .combineLatest(mousePosition$, (props, mousePosition) => ({ ...props, ...mousePosition }));
});

const DumbComponent = ({ x, y, mouseMove }) => (
  <div
    onMouseMove={mouseMove}
  >
    <span>{x}, {y}</span>
  </div>
);

const DumbComponentWithMouseMove = WithMouseMove(DumbComponent);

Original Post 原帖

For a slightly updated answer to the OP's updated answer, using rxjs5, I came up with the following: 对于使用rxjs5的OP更新答案略微更新的答案,我想出了以下内容:

class SomeComponent extends React.Component {
  constructor(props) {
    super(props);

    this.mouseMove$ = new Rx.Subject();
    this.mouseMove$.next = this.mouseMove$.next.bind(this.mouseMove$);

    this.mouseMove$
      .throttleTime(1000)
      .subscribe(idx => {
        console.log('throttled mouse move');
      });
  }

  componentWillUnmount() {
    this.mouseMove$.unsubscribe();
  }

  render() {
    return (
      <div
       onMouseMove={this.mouseMove$.next}
      />
    );
  }
}

Some notable additions: 一些值得注意的补充:

  • onNext() is now next() onNext()现在是next()
  • binding the observable next method allows it to be passed directly to the mouseMove handler 绑定observable next方法允许它直接传递给mouseMove处理程序
  • streams should be unsubscribed in componentWillUnmount hook 应该在componentWillUnmount挂钩中取消订阅流

Furthermore, the subject streams initialized in the component constructor hook can be passed as properties to 1+ child component(s), which could all push to the stream using any of the observable next/error/complete methods. 此外,在组件constructor钩子中初始化的主题流可以作为属性传递给1+子组件,这些子组件可以使用任何可观察的next / error / complete方法推送到流。 Here's a jsbin example I put together demonstrating multiple event streams shared between multiple components. 这是一个jsbin示例,我将它放在一起演示了多个组件之间共享的多个事件流。

Curious if anyone has ideas on how to better encapsulate this logic to simplify stuff like binding and unsubscribing. 好奇,如果有人有关于如何更好地封装此逻辑以简化绑定和取消订阅等内容的想法。

One option could be to use Rx.Observable.ofObjectChanges > cf. 一种选择可以是使用Rx.Observable.ofObjectChanges > cf. https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/ofobjectchanges.md . https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/ofobjectchanges.md

However : 但是:

  • It uses Object.observe which is not a standard feature, hence will have to be polyfilled in some browsers and is actually being removed from inclusion in ecmascript (cf. http://www.infoq.com/news/2015/11/object-observe-withdrawn ). 它使用的Object.observe不是标准功能,因此必须在某些浏览器中进行polyfilled,并且实际上已从ecmascript中删除(参见http://www.infoq.com/news/2015/11/object) - 观察 - 撤回 )。 Not the choice for the future, but it is easy to use, so if it is just for your own needs, why not. 不是未来的选择,但它易于使用,所以如果它只是为了您自己的需要,为什么不呢。

Other option is to use a subject in one of the three methods at your disposal according to your use case : shouldComponentUpdate , componentWillUpdate , componentDidUpdate . 其他选项是根据您的用例使用三种方法之一中的主题: shouldComponentUpdatecomponentWillUpdatecomponentDidUpdate Cf. 参看 https://facebook.github.io/react/docs/component-specs.html for when each function is executed. https://facebook.github.io/react/docs/component-specs.html用于执行每个函数的时间。 In one of these methods, you would check if this.state.val has changed, and emits its new value on the subject if it did. 在其中一种方法中,您将检查this.state.val是否已更改,并在该主题上发出新值。

I am not a reactjs specialist, so I guess they might be other options. 我不是reactjs专家,所以我猜他们可能是其他选择。

Although a subject will work, I think the best practice is to avoid using a subject when you can use an observable. 虽然一个主题可行,但我认为最好的做法是在你可以使用一个可观察的时候避免使用一个主题。 In this case you can use Observable.fromEvent : 在这种情况下,您可以使用Observable.fromEvent

class MouseOverComponent extends React.Component {

  componentDidMount() {
    this.mouseMove$ = Rx.Observable
      .fromEvent(this.mouseDiv, "mousemove")
      .throttleTime(1000)
      .subscribe(() => console.log("throttled mouse move"));

  }

  componentWillUnmount() {
    this.mouseMove$.unsubscribe();
  }

  render() {
    return (
      <div ref={(ref) => this.mouseDiv = ref}>
          Move the mouse...
      </div>
    );
  }
}


ReactDOM.render(<MouseOverComponent />, document.getElementById('app'));

Here it is on codepen .... 这是在codepen ....

It seems to me that there are other times when a Subject like the best choice, like when a custom React component executes a function when an event occurs. 在我看来,还有其他时候主题是最好的选择,比如当一个自定义的React组件在事件发生时执行一个函数。

I would highly recommend reading this blog post on streaming props to a React component using RxJS: 我强烈建议使用RxJS阅读关于流媒体道具的博客文章到React组件:

https://medium.com/@fahad19/using-rxjs-with-react-js-part-2-streaming-props-to-component-c7792bc1f40f https://medium.com/@fahad19/using-rxjs-with-react-js-part-2-streaming-props-to-component-c7792bc1f40f

It uses FrintJS , and applies the observe higher-order component for returning the props as a stream: 它使用FrintJS ,并应用observe高阶组件将道具作为流返回:

import React from 'react';
import { Observable } from 'rxjs';
import { observe } from 'frint-react';

function MyComponent(props) {
  return <p>Interval: {props.interval}</p>;
}

export default observe(function () {
  // return an Observable emitting a props-compatible object here
  return Observable.interval(1000)
    .map(x => ({ interval: x }));
})(MyComponent);

You can do it using hooks. 你可以用钩子做。

Here is a code sample 这是一个代码示例

import { Observable, Subscription } from 'rxjs';
import { useState, useEffect } from 'react';

export default function useObservable<T = number | undefined>(
    observable: Observable<T | undefined>,
    initialState?: T): T | undefined {
    const [state, setState] = useState<T | undefined>(initialState);

    useEffect(() => {
        const subscription: Subscription = observable.subscribe(
            (next: T | undefined) => {
                setState(next);
            },
            error => console.log(error),
            () => setState(undefined));
        return () => subscription.unsubscribe();
    }, [observable])

    return state;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM