React doesn't need RxJS
Tags: rxjs, react
Published: 2022-05-18 05:40:47

---
title: React doesn't need RxJS
published: true
description: RxJS is amazing, but in React, hooks do the job better.
tags: RxJS, React
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lqfc8wamq5bse7dagcbv.jpg
series: RxJS in React
---

RxJS is amazing. It simplifies asynchronous logic with its 100+ operators, such as `retry` and `debounce`.

But RxJS isn't just a bag of cool async utilities. RxJS also lets you write async code declaratively:
```typescript
// Without RxJS
// Logic is imperative and scattered
let results = [];

function handleSearch(search: string) {
  fetchData(search).then(data => results = data);
}

// With RxJS
// Logic is self-contained
const results$ = search$.pipe(switchMap(fetchData));
```

This is an under-appreciated superpower of RxJS, because centralizing logic with state dramatically reduces bugs.

When I learned React the first thing I did was create custom hooks for RxJS. At the time I couldn't find any examples to follow. I wondered why RxJS hadn't grown as popular in React as it had in Angular. But it turns out there was a good reason.

# Hooks

In 2018 I found myself at a React meetup in Lehi, Utah, looking at something really cool:
![Component class vs hooks](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xrbuvv4bqndzrzyd3oc5.png)
<figcaption>Not exactly this, but similar. Image Source: <a href="https://developer.epages.com/blog/coding/how-to-improve-your-components-with-react-hooks/">Jonathan Wieben</a></figcaption>

This is color-coded by feature/state, so it shows how class components (left) scatter your logic everywhere, whereas hooks (right) allow you to put your logic next to the state or feature it controls. 

This is the same benefit RxJS provides, as you saw above.

Both of these were in my brain for years, but I only realized last month that hooks are actually equivalent to RxJS operators! Yes, internally they are different, but they allow you to structure your code the same way, and that is what matters.

# RxJS operators as hooks: typeahead

Let’s start with a simple example: Typeahead! Here is how a typeahead would look with RxJS:
```typescript
const search$ = new Subject<string>();
const results$ = search$.pipe(
  debounceTime(500),
  filter(search => !!search.length),
  distinctUntilChanged(),
  switchMap(search => fetchItems(search}),
);
```

How do we translate that into hooks?

**`debounceTime`**: A few people have published debounce hooks. [Here's one.](https://usehooks.com/useDebounce/)

**`filter`**: Hooks cannot be conditionally called, so you can put this condition inside a `useEffect`. Or, if using React Query, you can pass in `{ enabled: false }` and it will not make the request.

**`distinctUntilChanged`**: `useEffect` will only run when `search` changes. And React Query’s `useQuery` stores queries by key, so if the same search term is passed in, it reuses the original query. 

**`switchMap`**: If you want to implement cancellation, in your own custom hook you can use `useEffect` like this:
```typescript
function useFetchItems(search: string) {
  const [result, setResult] = useState<Result>(initialResult);

  useEffect(() => {
    let req;
    if (search) req = fetchItems(search).then(setResult);
    return () => req?.cancel(); // Or whatever it is for the fetching client you’re using
  }, [search]);

  return result;
}
```

When a new search term comes in, the previous request is canceled and a new one is created. 

(For React Query, `useQuery` won’t cancel previous requests, but it will return the latest one, even if the server responds out of order, because it organizes by query key.)

Putting it all together, we get something just as reactive and declarative as RxJS:
```typescript
const [search, setSearch] = useState(‘’);
const debouncedSearch = useDebounce(search, 500);
const result = useFetchItems(debouncedSearch);
```

Now look at that! Just a bunch of declarative code, like RxJS! Beautiful.

# Why hooks are enough
RxJS streams are not stateless, pure functions; it’s just that the state is internal. How do you think you still have access to the previous value of each input stream when using `combineLatest`? What do you think happens to a value while the stream is waiting for `delayTime` to output? RxJS just takes care of this internal state for you, so all of _your_ code can be declarative and reactive. 

React hooks also abstract away the messy, asynchronous side effects so your components can stay simple and declarative. But each step in the state 'pipeline' is not hidden from you, but out there for you to use and see. This makes you come up with stupid names like `debouncedValue`, but it also allows for much easier debugging than RxJS allows. 

Speaking of `combineLatest`, what would it look like with hooks? First, here’s RxJS:
```typescript
const a$ = new BehaviorSubject(1);
const b$ = new BehaviorSubject(2);
const total$ = combineLatest(a$, b$).pipe(
  map(([a, b]) => a + b),
);
```

And with hooks:
```typescript
const [a, setA] = useState(1);
const [b, setB] = useState(2);
const total = a + b;
```

I actually prefer that!

# Challenge!
Give me something in RxJS and I will rewrite it with hooks!

In the future I might create a cheatsheet for all operators.

# Performance
Okay, this is an issue. RxJS is precise and efficient, while React is chaotic and overreactive. Some event sources fire extremely rapidly (like `mousemove`), which can make React's inefficiencies noticeable. In these situations you will want to bail out of React and directly manipulate DOM elements, using RxJS as needed:
```tsx
function MouseMoveExample() {
  const divRef = useRef();
  useEffect(() => {
    // Interact with the DOM element directly
    // Use RxJS for declarative async code
  }, [divRef])
  return <div ref={divRef}>asdf</div>
}
```

# Why React code still sucks

So if React can handle asynchronous logic in a completely declarative way, why is the default programming style still so often imperative? For example, in this comparison between Svelte and React, Svelte looks much cleaner and contains no imperative code: 

![React vs Svelte](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hg4vuu0b52uddjjubhwx.png)
<figcaption>Image Source: <a href="https://pagepro.co/blog/react-vs-svelte/">Emil Gawkowski</a></figcaption>

---

How is Svelte doing this???

First, notice that Svelte has provided special syntax for input events, whereas React has left us needing to drill down to `event.target.value`. Could React provide us a special hook specifically for input events? What would that hook look like?

We want our code to be completely reactive, so rather than calling callback functions that imperatively call `setA` or `setB`, we want something we can use like `onChange={specialHook}`. Here is the hook that I propose:
```tsx
function useNumberInputState(initialState: number) {
  const [state, setState] = useState(initialState);
  return [
    state,
    (event: ChangeEvent<HTMLInputElement>) => setState(+event.target.value)
    setState,
  ];
}
```

It can be used like this:
```tsx
function Demo() {
  const [a, changeA] = useNumberInputState(1);
  const [b, changeB] = useNumberInputState(2);
  
  return (
    <>
      <input type=”number” value={a} onChange={changeA} />
      <input type=”number” value={b} onChange={changeB} />

      <p>{a} + {b} = {a + b}</p>
    </>
  );
}
```

Here’s Svelte for comparison:
```svelte
<script>
  let a = 1;
  let b = 2;
</script>

<input type=”number” value={a} bind:value={a} />
<input type=”number” value={b} bind:value={b} />

<p>{a} + {b} = {a + b}</p>
```

Not bad.

Svelte is still more minimal, but there’s an issue with the way it achieves its minimalism: React has purposefully shunned two-way data binding, and they were right. In 2016 the Angular team agreed and removed it from Angular (although they later added it back with special syntax because of popular, misguided demand). What's the problem with it? It creates messy code because often multiple pieces of state need to update in response to a single event. With Svelte and React, at least you can update downstream state reactively with no issue. `{a + b}` in the template is a simple example of that. However, sometimes independent pieces of state need to update in response to the same event, so you either need a callback function with individual, imperative `setState` calls, or some a way to react to unique event objects (like Redux actions), which React and Svelte do not have quick, reactive solutions for. 

More on this in a future post!

# Going forward

The promise of hooks has never been fully realized, so what do we do now?

We should focus on using the power of hooks to eliminate imperative code from our apps. I might start a series of posts on how to write hooks to avoid callbacks, since callbacks are containers for imperative code.

I do not believe React is the future of web development. It still has many years ahead in the limelight, but it is too inefficient out of the box. I'm surprised at how often performance concerns muddy up component code. RxJS is just more _accurate_. 

But React might be the best option right now. I haven't seen a completely reactive framework yet. Svelte is a great idea, but declarative async code is only possible with RxJS, which admittedly is not difficult to use in Svelte; but Svelte wasn't designed to use RxJS as a primary technology, so the integration is slightly awkward to me. 

RxJS itself has some issues too. I mentioned the debuggability issue above. Another issue is extremely verbose syntax for state managed with `scan`. I created [StateAdapt](https://medium.com/weekly-webtips/introducing-stateadapt-reusable-reactive-state-management-9f0388f1850e) as a remedy for this. 

As for Angular, the best thing it could do is make all component lifecycle methods available as observables, as well as component inputs. Without this, Angular is one of the least reactive frameworks currently popular. As a long-time Angular developer, I would love to see them fix this, as well as [a few other issues.](https://betterprogramming.pub/angular-isnt-reactive-enough-70b6b8d6cf8)

---

The history of web development has trended towards more and more reactive/declarative code since the very beginning, and that will not stop.

Embrace it!