【问题标题】:How to bind React state to RxJS observable stream?如何将 React 状态绑定到 RxJS 可观察流?
【发布时间】:2015-11-16 00:30:16
【问题描述】:

有人可以帮我如何将 React State 绑定到 RxJS Observable 吗?我做了某事

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

理想的结果是,每当this.state.val 更新(通过this.setState(...)source 也得到更新,所以我可以将source 与其他RxJS 可观察流结合起来。

但是,在这种情况下,source 仅更新一次,即使在更新 this.state.val 并重新渲染组件之后也是如此。

// 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

这可能是因为componentDidMount() 在 React 生命周期中只调用了一次。所以我将source 移动到componentDidUpdate(),每次渲染组件后都会调用它。但是,结果仍然保持不变。

那么问题是如何让sourcethis.state.val 更新时更新?

更新:这是我用来解决问题的解决方案,使用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} />
}
// 

【问题讨论】:

    标签: javascript reactjs rxjs


    【解决方案1】:

    更新

    要抽象出以下一些复杂性,请使用 recompose 的 mapPropsStreamcomponentFromStream。例如

    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);
    

    原帖

    对于 OP 的更新答案的稍微更新的答案,使用 rxjs5,我想出了以下内容:

    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}
          />
        );
      }
    }
    

    一些值得注意的补充:

    • onNext() 现在是 next()
    • 绑定可观察的next 方法允许将其直接传递给mouseMove 处理程序
    • 应该在componentWillUnmount钩子中取消订阅流

    此外,在组件 constructor 钩子中初始化的主题流可以作为属性传递给 1+ 个子组件,这些子组件都可以使用任何可观察的 next/error/complete 方法推送到流。 Here's a jsbin example 我将展示多个组件之间共享的多个事件流。

    想知道是否有人对如何更好地封装此逻辑以简化绑定和取消订阅等内容有想法。

    【讨论】:

      【解决方案2】:

      一种选择是使用Rx.Observable.ofObjectChanges > cf。 https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/ofobjectchanges.md.

      但是:

      • 它使用Object.observe,这不是标准功能,因此必须在某些浏览器中进行polyfill,实际上已从ecmascript中删除(参见http://www.infoq.com/news/2015/11/object-observe-withdrawn)。不是未来的选择,但它易于使用,所以如果只是为了您自己的需要,为什么不呢。

      其他选项是根据您的用例以三种方法之一使用主题:shouldComponentUpdatecomponentWillUpdatecomponentDidUpdate。参照。 https://facebook.github.io/react/docs/component-specs.html 表示每个函数的执行时间。在其中一种方法中,您将检查 this.state.val 是否已更改,如果已更改,则在主题上发出其新值。

      我不是reactjs 专家,所以我想他们可能是其他选择。

      【讨论】:

      • 谢谢,我确实设法通过在您的回答之前使用Rx.Subject 来使用不同的方法。但我会以任何方式标记你的答案是正确的,因为它是正确的答案,知道有Rx.Observable.ofObjectChanges 很有趣。很遗憾知道Object.observe 被撤回了。
      • 我很高兴。您应该考虑在此处与有相同问题的未来观众分享您的方法。
      【解决方案3】:

      虽然可以使用主题,但我认为best practice 是为了避免在可以使用可观察对象时使用主题。在这种情况下你可以使用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'));
      

      这里是codepen....

      在我看来,在其他时候,Subject 是最好的选择,比如自定义 React 组件在事件发生时执行函数。

      【讨论】:

        【解决方案4】:

        我强烈推荐阅读这篇关于使用 RxJS 将 props 流式传输到 React 组件的博文:

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

        它使用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);
        

        【讨论】:

          【解决方案5】:

          你可以使用钩子来做到这一点。

          这是一个代码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;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-05-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多