React Query in Web Extensions
Tags: react, reactquery, webextensions, webdev
Published: 2021-02-08 21:31:20

[React Query](https://react-query.tanstack.com/) is a library for fetching data from API. Is performant, manages fetched data, and what is the most important is backend agnostic — you can use it with REST or GraphQL APIs. But what about where you didn’t have API at all. Did you ever wonder “Can We use react-query when I don’t have API?”.

---
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4ic8t6vwh3ojtncwsp6b.png)

Did you hear about [Bookmarkly](https://bookmarkly.app)? I’m working on a bookmark manager, which helps you organize your bookmarks. ML models help you boost your productivity. It’s totally free! You can find more on https://bookmarkly.app

---

## TL;DR

![yes, we can - brooklyn 9-9 gif](https://media.giphy.com/media/5qFZ8ozwhsdsaNUsNm/giphy.gif)

## But, how is that possible?

As I mention above react query is backend agnostic. It’s mean it’s not worrying about how we obtain data from the backend. We just need a key that represents our data and the Promise that after resolving fetches our data.
In this example, we are using https://github.com/mozilla/webextension-polyfill to add cross-browser support.

```javascript
import browser from 'webextension-polyfill';

export async function getSites() {
  const { sites } = await browser.storage.local.get(['sites']);

  return sites || [{ id: 1, name: 'sample website' }];
}
```

In react component, we can use react’s [fetch-as-render](https://reactjs.org/docs/concurrent-mode-suspense.html) experimental functionality, that’s why we don’t bother catching errors in the component.

```javascript
import { getSites } from './queries';

export const Sites = () => {
  const { data } = useQuery('sites', getSites);

  return (
    <div>
     {data.map(site => (
       <span key={site.id}>{site.name}</span>
     )}
    </div>
  )
}
```

To catch errors we are using [error boundaries](https://reactjs.org/docs/error-boundaries.html). We can create a sample component like this:

```javascript
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) { 
   return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // You can also log the error to an error
    // reporting service logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) { 
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

export ErrorBoundary;
```

Everything is composed like that:

```javascript
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { ReactQueryConfigProvider } from 'react-query';
import { Sites } from './sites';
import { ErrorBoundary } from './error-boundry';

const queryConfig = {
  suspense: true,
};

async function main(): Promise<Event> {
  return new Promise((resolve) => window.addEventListener('DOMContentLoaded', resolve));
}

function render() {
  ReactDOM.render(
    <ReactQueryConfigProvider config={queryConfig}>
      <ErrorBoundary>
        <Sites />
      </ErrorBoundary>
    </ReactQueryConfigProvider>,
    document.getElementById('popup'),
  );
}

main()
  .then(render)
  .catch(reportError);
```

## Summary

As you see, it’s easy to use react query in web extension. React Query has a fantastic developer experience. That’s why I’m using it in [Bookmarkly](https://bookmarkly.app/).