Bookmarkly use cookies to provide analysis of site usage and traffic measurement. Data is collected anonymously. See our Privacy Policy for more information about cookies.

React Query in Web Extensions

| 2 min read

React Query 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?”.


Bookmarkly is using it to serve always up-to-date bookmarks in extension. Using ML models helps you organize bookmarks and boost your productivity. It’s totally free!


TL;DR

A scene from Brooklyn 9–9 — great tv show :)

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.

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 experimental functionality, that’s why we don’t bother catching errors in the component.

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. We can create a sample component like this:

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:

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 fantastic developer experience. You should try it yourself.