Recoil.js
Tags: react, typescript, recoiljs
Published: 2022-11-17 10:40:15

Redux is pretty awesome. Well, the idea behind redux is pretty awesome (go ahead and read [Human Redux](https://read.reduxbook.com), it's great!). But the implementation, and React-integration, leads to lots of files, biolerplate and hassle [personal opinion, feel free to disagree].

So, when something new[^1] comes along that promises to solve those issues, it has to be explored. And so I did, a year ago or something. [Recoil.js](https://recoiljs.org/) brands itself as “A state management library for React” that is “Minimal and Reactish”.

Sounds good? I was sceptical, but after using it for about a year I very much like what I see. My main gripe with it is perhaps a certain lack of excamples, so to alleviate that some examples might be what’s called for.

So, why recoil?

Well, for a starter, consider this component

```ts
import React, {useState} from 'react';

export const MyCounter = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      <div>Count: {count}</div>
      <button onClick={() => setCount((count) => count + 1)}>Increment</button>
    </div>
  );
};
```

A bog-standard, boring, counter.

But, now we want to refactor this and split it into a button-component and a display component


```ts
import React, {useState} from 'react';

interface MyCounterButtonProps {
  increment: () => void;
}
const MyCounterButton = ({increment}: MyCounterButtonProps) => 
  <button onClick={() => increment()}>Increment</button>;

interface MyCounterDisplayProps {
  value: number;
}
const MyCounterDisplay = ({value}: MyCounterDisplayProps) => 
  <div>Count: {value}</div>;

export const MyCounter = () => {
  const [count, setCount] = useState(0);
  return (
    <div>
      <MyCounterDisplay value={count} />
      <MyCounterButton increment={() => setCount((count) => count + 1)} />
    </div>
  );
};
```
Well, this works. But we have to pass props, and these components are coupled. Surely there is a better way?

This is where recoil comes into play, if we use an _atom_:

```ts
import React from 'react';
import {atom, useRecoilValue, useSetRecoilState} from 'recoil';

const counterState = atom<number>({
  key: 'counterState',
  default: 0,
});

const MyCounterButton = () => {
  const setCounter = useSetRecoilState(counterState);
  return <button onClick={() => setCounter((count) => count + 1)}>Increment</button>;
};

const MyCounterDisplay = () => {
  const value = useRecoilValue(counterState);
  return <div>Count: {value}</div>;
};

export const MyCounter = () => (
  <div>
    <MyCounterDisplay />
    <MyCounterButton />
  </div>
);
```
See how we moved the state from the ``MyCounter`` component? Instead we put it into an atom, which we can access using the ``useRecoilValue`` hook, and set using the ``useSetRecoilState`` hook. 

This allows us to share state between components without passing it as props. Big deal? Well, not in this case perhaps, but we avoid long prop chains, and we are able to share state between components that are far apart from each other in the component tree. 

But, wait, is recoil just useState on streoids?

No, by no means! Recoil is a fully-fledged state managemnent library, with support for async queries and reducers. But we have to start somewhere!

This post is already getting long though, so we'll just cover one (simple) example of derived state.

Say we wanted our counter to display a message when the count was an even number.

Easy:
```tsx
const IsEvenDisplay = () => {
  const value = useRecoilValue(counterState);
  return <div>{value % 2 == 0 ? "even!": null}</div>;
}
```

This is of course really trivial, but say we had a more complex computation, that was unnatural to compute in a component. 

Then we could use a recoil ``selector``, and compute it there: 

```tsx
const isCounterEvenState = selector<boolean>({
  key: "isCounterEvenState",
  get: ({get}) => get(counterState) % 2 === 0
});

const IsEvenDisplay = () => {
  const isCounterEven = useRecoilValue(isCounterEvenState);
  return <div>{isCounterEven ? "even!": null}</div>;
}
```

Clean, eh?

And of course, there are lots of other aspects to recoil. How do we handle async queries? How do we test this? How do we organize things?

Those are all interesting subjects that we'll touch upon in a later installment. 

For now: If you haven't played around with recoil I strongly recommend that you do. The examples shown here are available at the first, half-decent, codepen-lookalike I could find: [here](https://playcode.io/1013494).

Installments in this series:
- Recoil.js (this post)
- [Recoil.js async selectors](https://dev.to/atlefren/recoiljs-async-selectors-42gc)


[^1]: "New" is a very relative term.