Recoil - The Asynchronous way to manage state [ Part 1 ]
Tags: react, javascript, webdev, beginners
Published: 2020-05-20 22:05:22

In previous [post](https://dev.to/shubhamk/recoil-facebook-s-own-state-management-library-26pb) we got an overview of some Recoil terminologies and how to manage state using it. 

Here, we will explore _**Asynchronous side of Recoil.**_ 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5ndkuypi8p86zfpx0qeh.gif)

_**Recoil asynchronous state management supports -**_

1. React ``<Suspense/>``. The fallback UI is rendered till the pending request is completed.
2. Without React ``<Suspense/>``, status of data from Recoil hooks can be used to check if it's still _loading_ or _completed_ or have caught an _error_. 

_**Let's create an app that would fetch and display data from an API**_

_To start with, Create a new ``create-react-app`` and clean it for a new project and wrap ``<RecoilRoot/>`` around the root._

**1. Let's start with writing a ``Selector`` which will fetch data.**

```javascript
import React from 'react';
import { selector } from 'recoil';

const url = `https://reqres.in/api/users?page=1`;

const fetchUserDetails = selector({
    key: 'userDetailsSelector',
    get: async ({ get }) => {
        try{
            const response = await fetch(url);
            const data = await response.json();
            return data;
        }catch(error){
            throw error;
        }
    }
});

function App() {
  return (
    <div> 
        <p> Recoil Example </p>
    </div>
  );
}

export default App;
```
* Using ``selector`` we fetch data with ``fetch`` .
* We set an ``async`` function to ``get`` parameter which will return the fetched data. 
* We can use value from ``atom`` to set URL parameter or body data like _user id_, _page number_ and _auth key_ but we skip it this time.  

**2. We create a component called ``<DetailsWithSuspense/>`` which would subscribe to ``fetchUserDetails Selector`` and render data.**

```javascript
import React from 'react';
import { selector, useRecoilValue } from 'recoil';

const url = `https://reqres.in/api/users?page=1`;

const fetchUserDetails = selector({
    key: 'userDetailsSelector',
    get: async ({ get }) => {
        try{
            const response = await fetch(url);
            const data = await response.json();
            return data;
        }catch(error){
            throw error;
        }
    }
});

const DetailsWithSuspense = () => {
    const userDetails = useRecoilValue(fetchUserDetails);
    const { data } = userDetails;
    
    return (
        data.map(item => (
            <div key={item.id}>
                <p>
     {`Email: ${item.email} Name: ${item.first_name} ${item.last_name}`}. 
                </p>
            </div>
        ))
    );
}


function App() {
  return (
    <div> 
        <p> Recoil Example </p>
    </div>
  );
}

export default App;
```

* Here we use ``useRecoilValue`` hook to subscribe and get the value of the ``fetchUserDetails Selector``.
* But, we can also use ``useRecoilState`` hook to get the value and a function to set the value. ( Here, we can't set the value as data returned by selector is **Read Only** )

**3. Further, Let's add ``<Suspense/>`` to render asynchronous data**

```javascript
import React from 'react';
import { selector, useRecoilValue } from 'recoil';

const url = `https://reqres.in/api/users?page=1`;

const fetchUserDetails = selector({
    key: 'userDetailsSelector',
    get: async ({ get }) => {
        try{
            const response = await fetch(url);
            const data = await response.json();
            return data;
        }catch(error){
            throw error;
        }
    }
});

const DetailsWithSuspense = () => {
    const userDetails = useRecoilValue(fetchUserDetails);
    const { data } = userDetails;
    
    return (
        data.map(item => (
            <div key={item.id}>
                <p>
     {`Email: ${item.email} Name: ${item.first_name} ${item.last_name}`}. 
                </p>
            </div>
        ))
    );
}


function App() {
  return (
    <div> 
        <React.Suspense fallback={<div>Loading...</div>}>
           <DetailsWithSuspense />
        </React.Suspense>
    </div>
  );
}

export default App;
```

* We wrap the ``<DetailsWithSuspense />`` with ``<Suspense/>`` which takes care of pending data while ``fallback`` component is rendered till the asynchronous call is completed or have errors.

* To create an Error Handling Component, refer to [Error Boundaries](https://reactjs.org/docs/error-boundaries.html).

###If ``<Suspense/>`` is not your way, Recoil still got your back! :point_down:

 **4. We create and add another component called ``<DetailsWithoutSuspense />`` which would subscribe to ``fetchUserDetails Selector`` and render data.**

```javascript
import React from 'react';
import { selector, useRecoilValue, useRecoilValueLoadable } from 'recoil';

const url = `https://reqres.in/api/users?page=1`;

const fetchUserDetails = selector({
    key: 'userDetailsSelector',
    get: async ({ get }) => {
        try{
            const response = await fetch(url);
            const data = await response.json();
            return data;
        }catch(error){
            throw error;
        }
    }
});

const DetailsWithoutSuspense = () => {

    const userDetails = useRecoilValueLoadable(fetchUserDetails);
    const { state } = userDetails;
    
    if (userDetails.state === 'hasError') {
        return <div> There is some problem! </div>
    }

    if(state === 'loading'){
        return <div>Its loading</div>
    }

    if(state === 'hasValue'){
        const { contents: { data }} = userDetails;
        return (
            data.map(item => (
                <div key={item.id}>
                    <p>
     {`Email: ${item.email} Name: ${item.first_name} ${item.last_name}`}.   
                   </p>
                </div>
            ))
        );
                    
    }
}

const DetailsWithSuspense = () => {
    const userDetails = useRecoilValue(fetchUserDetails);
    const { data } = userDetails;
    
    return (
        data.map(item => (
            <div key={item.id}>
                <p>
     {`Email: ${item.email} Name: ${item.first_name} ${item.last_name}`}. 
                </p>
            </div>
        ))
    );
}


function App() {
  return (
    <div> 
        <DetailsWithoutSuspense />
        <React.Suspense fallback={<div>Loading...</div>}>
           <DetailsWithSuspense />
        </React.Suspense>
    </div>
  );
}

export default App;
```

* We use ``useRecoilValueLoadable`` hook to subscribe to ``fetchUserDetails Selector``.

* Further, ``useRecoilValueLoadable`` returns an object with ``state`` key, which holds current status of pending data which can be either -

      a. ``hasError`` : set when an error occurs
      b. ``loading``  : set when data is pending
      c. ``hasValue`` : set when data is received successfully

* Depending on ``state`` value, a component can be rendered accordingly.

* When ``state`` value is set to ``hasValue``, the object returned by ``useRecoilValueLoadable`` holds the data that was pending in ``contents`` key.

This would complete the little Fetch Data app which gets data asynchronously using Recoil APIs. If you want to see a more structured approach, checkout GitHub repository below. 

{% github shubhaemk/recoil-async-example no-readme%} 

Next I will be exploring ``selectorFamily`` which is similar to ``selector`` but accepts a parameter.

At the end, I would like to thanks [Reqres](https://reqres.in/) for giving APIs to test.