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?”.
Did you hear about Bookmarkly? 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
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 a fantastic developer experience. That’s why I’m using it in Bookmarkly.
Top comments (6)
I think I do a similar extension, I work on it for the last 5 months, also the same stack as to, I use ReactQuery and React. One question why class components?
I use React Query with experimental functionality reactjs.org/docs/concurrent-mode-s.... To make it work correctly we need to set up error boundaries. They are only available with class syntax.
Although using class components itself isn't bad at all.
I'm really curious, do you mind share what are you working on?
Very cool. Sorry for now it is closed source because I have some unique features which I want to keep closed until I publish the extension. How about yours can I try it?
Not yet, I'm really close to the release date but there is a lot of things to do before that. I will let you know when it will be ready :)
Thanks, did you start with a react browser extension boilerplate or a totally custom webpack react project?
I used some "react chrome extension boilerplate" but I can't find it now. But all things were done using web webpack + react + typescript. When I went through coding I find it's useful to separate UI so it's easily replaceable.
Nonetheless, I made some PoC chrome extension using snowpack + preact and it is really cool to play with. You can find it here if you want to take a look: github.com/ZBW-dev/pullrequests-te...