Last month at the React Conf, the React core team first brought hooks to the public’s attention. At first I was resistant to such a strange thing. Dan: The “this” in JavaScript is so dark that people who write React from other languages don’t get used to it. However, hooks are essentially dark magic, and understanding them still requires a thorough understanding of the various CLOSURES and scopes of JS.

Later, however, after spending a few days with hooks, I found the idea interesting. React Today and Tomorrow and 90% Cleaner React With Hooks.

Our team has always loved RxJS, but it was a bit of a hassle to combine it with React. Last week,

@ Wolf


rxjs-hooks

So how sweet is it to write RxJS code using hooks? Leetcode-opensource /rxjs-hooks let’s take a look at this OpenSource project that keeps mom happy and on and on: leetcode-opensource /rxjs-hooks


Rxjs-hooks have complete test cases with 100% test coverage. Currently there are only two hooks: useObservable and useEventCallback. Let’s first recall how to create, subscribe to, and destroy a stream in the React Component. Something like this:

import React from 'react'; import { interval } from 'rxjs'; import { tap } from 'rxjs/operators'; class Timer extends React.Component { state = { val: 0, }; subscription = new Subscription(); componentDidMount() { const sub = interval(1000).pipe( tap((val) => this.setState({ val })) ) this.subscription.add(sub)  } componentWillUnmount() { this.subscription.unsubscribe() } render() { return <h1>{this.state.val}</h1> } }Copy the code

Subscribe manually, manage the declaration cycle manually, and bridge the render function (UI) with state in React. What about using rxjs-hooks:

import React from 'react';
import { interval } from 'rxjs';
import { useObservable } from 'rxjs-hooks';

function Timer() {
  const val = useObservable(() => interval(1000), 0);

  return <h1>{val}</h1>
}
Copy the code

There are no manual subscriptions and no need to bother with lifecycle management. With a dependency of less than 1KB, you can happily embrace RxJS in the React world.

API,

This section briefly introduces two apis in rxJs-hooks with some examples. Detailed type definitions can be viewed here. The following will be combined with examples to explain, so it should be easier to understand.

Pay attention to

  • The following examples are based on RxJS 6
  • If you don’t know much about React hooks, check out this video or the React blog.

1. useObservable

Case 1: No default value, independent of external state

function Timer() {
  const val = useObservable(() => interval(1000));

  return <h1>{val}</h1>
}
Copy the code

In this case, only the first parameter is passed, which is the Factory function of an Observable that needs to return an Observable, and useObservable’s return value is always the latest value of the stream. The first render has only one empty

; After 1 second, the content becomes 0; After 2 seconds, the content changes to 1…

Case 2: There are default values

function Timer() {
  const val = useObservable(() => interval(1000), -1);

  return <h1>{val}</h1>
}
Copy the code

In the second case, we pass the second parameter, which is the default value for val. So in this case, the first render is no longer empty, but -1.

Case 3: Relying on the last execution status

If you need to get the result of the last output in the stream, the factory function passes a state$stream to help you do so. (Be sure to use withLatestFrom to combine the stream, otherwise an infinite loop will occur.)

function Timer() {
  const val = useObservable((state$) => interval(1000).pipe(
	withLatestFrom(state$),
	map(([index, prevVal]) => index + prevVal),
  ), 0);

  // first render:    0
  // 1s later:        1    (1 + 0)
  // 2s later:        3    (2 + 1)  
  // 3s later:        6    (3 + 3)
  // 4s later:       10    (4 + 6)
  return <h1>{val}</h1>
}
Copy the code

Case 4: Dependence on external states

The factory function can rely on some external incoming state, passed in as the third parameter of the useObservable (similar to useEffect).

If the third argument is passed, then the factory function gets two streams, input$and state$. In the following example, the value emitted by the input$stream is always a [A, b] tuple. To make the example easier to understand, we will not use the state$stream for now.

function Timer({ a }) {
  const [b, setB] = useState(0);
  const val = useObservable(
    (inputs$, _state$) => timer(1000).pipe(
      combineLatest(inputs$),
      map(([_, [a, b]]) => a + b),
    ),
    0,
    [a, b],
  );

  return (
    <div>
      <h1>{val}</h1>
      <button onClick={() => setB(b + 10)}>b: +10</button>
    </div>
  )
}

function App() {
  const [a, setA] = useState(100);

  return (
    <div>
      <Timer a={a} />
      <button onClick={() => setA(a + 100)}>a: +100</button>
    </div>
  );
}
Copy the code

This example is relatively complex and can be understood with a live demo.

2. useEventCallback

We believe that RxJS is not only very good at handling data flows, but also very helpful in handling some interactive logic. So we designed a second API, useEventCallback, that takes three arguments. The last two parameters have a lot in common with useObservable, so I’ll focus on the return value of the first form participation.

First look at the following example (live demo). It is easy to see that the return value is not the same as useEventCallback, which returns a [callback, value] tuple. The factory function, which takes an event$parameter, will always have a new value flowing out of the event$stream each time a callback is called. The second argument to the useEventCallback function is still the familiar default value.

function App() {
  const [clickCallback, [description, x, y]] = useEventCallback((event$) =>
    event$.pipe(
      map((event) => [event.target.innerHTML, event.clientX, event.clientY]),
    ),
    ["nothing", 0, 0],
  )

  return (
    <div className="App">
      <h1>click position: {x}, {y}</h1>
      <h1>"{description}" was clicked.</h1>
      <button onClick={clickCallback}>click me</button>
      <button onClick={clickCallback}>click you</button>
      <button onClick={clickCallback}>click him</button>
    </div>
  );
}
Copy the code

More practical examples

Here are some simple practical examples to help you understand the use of rxjs-hooks. The code will not be posted in the body, but if you are interested, you can visit the online link in the example below to play.

Example 1: Drag me

live demo


Case: two column resizable layout

live demo


Example: Trailing queues

live demo


summary

So much for rxjs-hooks. Our implementation is not necessarily the best understanding of hooks. We look forward to seeing more people in the community get involved in this change and we’d love to share our experiences with you. At the same time, you are welcome to give an issue or PR for this project.


At present, the LeetCode team has both a project developed in cooperation with the American team and an independent product line in China. The team size is relatively small but good, excellent and potential members are welcome to join.

Job opening and salary range:

  • Python backend engineers 20K-40K
  • Junior Front End Engineer 18K-25K
  • Senior Front End Engineer 25K-45K
  • Full stack engineer 25K-45K

Submit your resume: Join us – LeetCode or email me directly at [email protected]