Recoil.js async selectors
Tags: react, recoiljs, typescript, async
Published: 2022-11-18 10:29:57

In [the first post on Recoil.js](https://dev.to/atlefren/recoiljs-2d4j) I touched upon `atom`, which is a piece of state that can be read and written. 

Then I followed up by showing how a `selector` can be used to derive state from atoms. 

This is all fine. But Atle, ever since the days of `XMLHttpRequest`, JavaScript has been used to fetch data from APIs. And in 2022 I expect to be able to do this async!

I hear you!

And in this post I'm going to cover how to do exactly this.

Say we have this API for retrieving blog posts:

```ts
export interface BlogPostHeader {
  title: string;
  id: number;
}

export interface BlogPost extends BlogPostHeader {
  content: string;
}

export interface BlogApi {
  getPosts: () => Promise<BlogPostHeader[]>;
  getPost: (id: number) =>  Promise<BlogPost | undefined>;
}
```
Nice, eh? Well, good enough to serve our illustration purposes I guess.


But having an API isn't enough, we need to be able to show data, right?

So you guessed it, we need those blog posts in state. And, how do we do that?

Using a selector, of course!

And, the get method of a selector can of course be async:
```ts
const blogPostListState = selector<BlogPostHeader[]>({
  key: "blogPostListState", 
  get: async ({get}) => await api.getPosts()
});
```

The observant reader may ask: "where do you get the `api` object from?". Let's just say that for now this is a singleton, ok? Bear with me, please.

OK, we have an api, and we have this represented as a selector. How do we use it?

Using `useRecoilValue` of course!

But, the selector is async? 

Yeah, right, we have to wrap things in a React `<Suspense>` block. Like so:

```ts
const BlogPostList = () => {
  const posts = useRecoilValue(blogPostListState)
  return <ul>{posts.map(post => <li key={post.id}>{post.title}</li>)}</ul>
}

export const MyBlog = () =>  (
  <>
    <h1>My awesome blog</h1>
    <Suspense fallback="loading"><BlogPostList /></Suspense>
  </>
);
```

Or, you could use `useRecoilValueLoadable` and get a [loadable](https://recoiljs.org/docs/api-reference/core/Loadable) back. This leaves more control up to you, and can be handy in some situations.  But that is a topic for another day. We'll stick with Suspense for now.

But we want the user to be able to select a specific blog post as well, right?

Yep! We could store the selected post id in an `atom` and call getPost in another `selector`. Like so:

```ts
const selectedPostState = atom<number | undefined>({
  key: "selectedPostState",
  default: undefined
});

const selectedPostContentState = selector<BlogPost | undefined>({
  key: "selectedPostContentState",
  get: async ({get}) => {
    const id = get(selectedPostState);
    return id ? await api.getPost(id) : undefined;
  }
});
```

And then we just have to wire this up. With some refactoring, because our components are getting rather messy:

```ts
const BlogPostListElement = ({post}: {post: BlogPostHeader}) => {
  const setSelected = useSetRecoilState(selectedPostState);
  return (
    <li onClick={() => setSelected(post.id) }>
      {post.title}
    </li>
  );
}

const BlogPostList = () => {
  const posts = useRecoilValue(blogPostListState)
  return (
    <ul>
      {posts.map(post => <BlogPostListElement key={post.id} post={post} />)}
    </ul>
  );
}
```
Don't you just _love_ the misuse of list elements? Well. I never claimed I had an iota of design-capabilities. But you get the gist here. And I'm on a tangent. 

To get back on track: What's going on here? When the user clicks a blog post we set the `selectedPostState`. So, what remains is to create a component to show the selected blog post (if any):

```ts
const BlogPostContent = () => {
  const post = useRecoilValue(selectedPostContentState);
  return post 
    ? <>
        <h2>{post.title}</h2>
        <p>{post.content}</p>
      </> 
    : null;
}
```

And then we have to use this component in our app, wrapped in Suspense of course:

```ts
export const MyBlog = () => (
  <>
    <h1>My awesome blog</h1>
    <Suspense fallback="loading"><BlogPostList /></Suspense>
    <Suspense fallback="loading"><BlogPostContent /></Suspense>
  </>
);
```

And there you have it. Bob is your fathers brother and all that. 

But what is going on?

Ok, we have three pieces of state

- `selectedPostState`: an atom containing the id of the selected post (if any)
- `blogPostListState`: an async selector that fetches the list of blog posts. This will only be evaluated once, as it has no dependencies
- `selectedPostContentState`: an async selector that fetches the selected blog post if set. This will re-evaulate each time `selectedPostState`, as it depends on it. 

And each of our components interacts with the pieces of state that they need.

I find this very clean and easy to reason about, and hopefully you'll now have a better understanding of how recoil can be used to deal with async state. 

The code for this is available at [playcode.io](https://playcode.io/1014038), which started bitching about exceeding the free tier, so the code isn't as clean and separated as I wanted, so for the next installment I'll find another codepen. Maybe codepen? ;)

Because, yes, there _will_ be a next installment. We have so many other topics to cover, such as

- Organization of a project
- selectorFamilies
- error handling
- testing
- initializeState
- Loadables
- crazy chaining

And more. So, check back or subscribe or whatever ;)