简体   繁体   English

如何将 RxJs 连接到 React 组件

[英]How to connect RxJs to React component

Most examples for using RxJS to observe button clicks are like:大多数使用 RxJS 观察按钮点击的例子如下:

var button = document.querySelector('button');
Rx.Observable.fromEvent(button, 'click')
  .subscribe(() => console.log('Clicked!'));

Is this okay in React?这在 React 中可以吗? Since there is the virtual DOM, is it still okay to get reference to the real DOM like this?既然有虚拟 DOM,那么像这样引用真实 DOM 还可以吗? What happens after a re-render?重新渲染后会发生什么? Is the subscription lost?订阅丢失了吗?

Ie they use document.querySelector .即他们使用document.querySelector But when I write my render() method, I'm used to <button onClick={...} > .但是当我编写我的 render() 方法时,我习惯于<button onClick={...} > What is the recommended way to do this in React?在 React 中推荐的方法是什么? Get rid of the onClick inside the JSX, add a class/id to my button, or maybe a ref, and use the style above?摆脱 JSX 中的 onClick,向我的按钮添加一个类/ID,或者可能是一个引用,然后使用上面的样式?

Also, should I unsubscribe anywhere, eg in componentWillUnmount() ?另外,我应该在任何地方取消订阅,例如在componentWillUnmount()中吗? In which case I'd want to keep references to all my subscribers (event listeners)?在这种情况下,我想保留对所有订阅者(事件侦听器)的引用? In which case this seem much more complex than the old (callback) way.在这种情况下,这似乎比旧的(回调)方式复杂得多。

Any suggestions, thoughts on this?对此有什么建议和想法吗?

I've thought about this a lot - I think the answer by @ArtemDevAkk is one really good way to do it, but I'm not sure it accounts for how you intend to use the data.我已经考虑了很多 - 我认为@ArtemDevAkk 的答案是一种非常好的方法,但我不确定它是否说明了您打算如何使用数据。 I'd like to suggest an alternative approach.我想提出一种替代方法。 Admittedly, I've used hooks for this, but you could do this in old school classes in a similar way.诚然,我为此使用了钩子,但你可以在老学校的课堂上以类似的方式做到这一点。

I've had to break up your question into a few parts:我不得不把你的问题分成几个部分:

  1. How to reference a DOM node in React如何在 React 中引用 DOM 节点
  2. How to create a Subject and make it fire如何创建主题并使其触发
  3. How to subscribe to any Observable in React (useEffect)如何订阅 React 中的任何 Observable (useEffect)

Getting a reference to a DOM node获取对 DOM 节点的引用

In case all you are asking is how to reference a DOM node, the rule of thumb is that to access the DOM in React, you use the useRef hook ( or createRef in a Class ).如果您要问的只是如何引用 DOM 节点,经验法则是在 React 中访问 DOM,您使用useRef 钩子或类中的 createRef )。

Keeping your Subject active (for sharing events)保持您的主题活跃(用于共享事件)

The benefit of this method is that your Subject will be created once and kept alive indefinitely, so anything is able to subscribe and unsubscribe at will.这种方法的好处是您的主题将被创建一次并无限期地保持活动状态,因此任何东西都可以随意订阅和取消订阅。 I can't think of a great reason for using RxJs in a React project because React has its own ways to handle events, so it's hard to know if this will solve your problem.我想不出在 React 项目中使用 RxJs 的充分理由,因为 React 有自己的方式来处理事件,所以很难知道这是否能解决你的问题。

function MyRxJsComponent() {

    // establish a stateful subject, so it lives as long as your component does
    const [ clicks$ ] = useState(new Subject());

    // register an event handler on your button click that calls next on your Subject
    return <button onClick={e => clicks$.next(e)}></button>
}

This will create one Subject that will stay alive as long as your component is alive on the page.这将创建一个主题,只要您的组件在页面上处于活动状态,该主题就会保持活动状态。 My best guess for how you might use this is by using a Context to contain your subject (put clicks$ in your context) and then pushing events to that Subject.我对如何使用它的最佳猜测是使用 Context 来包含您的主题(将clicks$放在您的上下文中),然后将事件推送到该主题。

Note how I'm not using fromEvent to create an Observable, because this Observable would be created and destroyed with your button, making it impossible for anything to stay subscribed to it outside the component.请注意我没有使用 fromEvent 来创建 Observable,因为这个 Observable 将使用您的按钮创建和销毁,这使得任何东西都无法在组件之外保持订阅它。

Just using an Observable internally仅在内部使用 Observable

Alternatively (as @ArtemDevAkk alluded to), you could just create your Observable for local use and accept the limitation that it will be created and destroyed (this might actually be preferable, but again, it depends on what you're actually trying to achieve).或者(正如@ArtemDevAkk 所暗示的那样),您可以只创建您的 Observable 供本地使用并接受将创建和销毁它的限制(这实际上可能更可取,但同样,这取决于您实际想要实现的目标)。

function MyRxJsComponent() {

    // get a reference to the button (use <button ref={buttonRef} in your component)
    const buttonRef = useRef(null)
    
    // use useEffect to tell React that something outside of its 
    // control is going to happen (note how the return is _another_ 
    // function that unsubscribes.
    useEffect( () => {

        // create the observable from the buttonRef we created
        const clicks$ = fromEvent(buttonRef.current, 'click')

        clicks$.subscribe( click => {

            // do whatever you need to with the click event
            console.log('button was clicked!', click)
        })
    
        return () => {
            start$.unsubscribe()
        }
    }, [buttonRef]);

    return <button ref={buttonRef}>Click me!</button>;
}

But should you?但是你应该吗?

I think overall, I've shown above how you can use state to keep a Subject alive for as long as you need;我认为总的来说,我已经在上面展示了如何使用状态来让主题保持活动状态,只要你需要; you can use useEffect() to subscribe to an observable and unsubscribe when you don't need it any more.您可以使用 useEffect() 订阅可观察对象并在不再需要时取消订阅。 (I haven't tested the samples locally, so there might be a small tweak needed..) (我没有在本地测试过样本,所以可能需要做一些小调整..)

I mentioned using a Context to share your Subject with other components, which is one way I can imagine RxJs being useful in React.我提到使用 Context 与其他组件共享您的主题,这是我可以想象 RxJs 在 React 中有用的一种方式。

Ultimately, though, if this is all you want to do, there are better ways to handle events in React.不过,最终,如果这就是你想做的所有事情,那么在 React 中有更好的方法来处理事件。 A simple <button onClick={() => console.log('clicked')}> could be all you need.一个简单的<button onClick={() => console.log('clicked')}>可能就是你所需要的。 React, being a reactive library in itself, isn't really designed to contain the amount of state that an Observable can contain - your views are meant to be simplified projections of state stored elsewhere. React 本身就是一个响应式库,并没有真正设计为包含 Observable 可以包含的状态量 - 您的视图旨在简化存储在其他地方的状态投影。 For the same token, it's generally advised that you don't try to reference a specific element unless you have an exceptional reason to do so.出于同样的原因,通常建议您不要尝试引用特定元素,除非您有特殊原因这样做。

const buttonRef = useRef(null)
useEffect( () => {
    const start$ = fromEvent(buttonRef.current, 'click').subscribe( click => {
        console.log('click event :', click)
    })

    return () => {
        start$.unsubscribe()
    }
}, [])

add ref to button将引用添加到按钮

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

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