Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to connect RxJs to React component

Most examples for using RxJS to observe button clicks are like:

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

Is this okay in React? Since there is the virtual DOM, is it still okay to get reference to the real DOM like this? What happens after a re-render? Is the subscription lost?

I.e. they use document.querySelector. But when I write my render() method, I'm used to <button onClick={...} >. What is the recommended way to do this in 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?

Also, should I unsubscribe anywhere, e.g. in 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?

like image 753
specimen Avatar asked Dec 15 '17 12:12

specimen


People also ask

Can you use RxJS With React?

Compared to other alternatives like Redux, I've found the use of RxJS and Hooks to be a really effective and straightforward way to manage state in React applications.

Can I use observables in React?

RxJS, a library for reactive programming in JavaScript, has a concept of observables, which are streams of data that an observer can subscribe to, and this observer is delivered data over time. An observer of an observable is an object with three functions: next , error , and complete .

Is RxJS for angular or React?

RxJS (Reactive Extensions for JavaScript) is the fundamental library for Functional Reactive Programming (FRP) for a large number of popular JavaScript frameworks including Angular, React and NestJS.

Is React JS and RxJS same?

React is a javascript library for building user interfaces and makes it painless to create interactive UIs. RxJS is a javascript library for reactive programming using Observables and makes it painless to compose asynchronous or callback-based code.


1 Answers

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. 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
  2. How to create a Subject and make it fire
  3. How to subscribe to any Observable in React (useEffect)

Getting a reference to a DOM node

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

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.

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.

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.

Just using an Observable internally

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

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. (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.

Ultimately, though, if this is all you want to do, there are better ways to handle events in React. A simple <button onClick={() => console.log('clicked')}> could be all you need. 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. 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.

like image 137
Lawrence Job Avatar answered Oct 03 '22 03:10

Lawrence Job