One such library is React Query. It is a vast and powerful tool, with almost 1.4 million downloads on npmjs.com, which is used to update the application state relative to the server state. But what does it mean, another alternative to handle the application state? I will answer this question in today's blog.

What is a React Query?

The authors of this library themselves describe it as "the missing data-fetching library for React, but in more technical terms, it makes fetching, caching, synchronizing, and updating server state in your React applications a breeze.” I think this overview perfectly describes the library itself and its purpose of it.

Why is it so popular, and why is it worth using, in my opinion? It is based on the straightforward process of adapting our project to this library. Just configure this library, literally in 3 lines of code, to be able to use it across the application.

You may also like: What is a boilerplate? Meaning, history and examples

What does the initialization look like?

As I mentioned, starting an adventure with React Query is trivial. Basically, install the package and then declare components that will do almost everything. The most basic case looks like this:

import {QueryClient, QueryClientProvider} from 'react-query';
import {useList, useUserData, providers} from './src/constate';
import Compose from './src/utils/compose';

const queryClient = new QueryClient();

const App = () => {
  return (
    <SafeAreaView style={styles.safe}>
      <QueryClientProvider client={queryClient}>
        <Compose components={providers}>
          <Home />
        </Compose>
      </QueryClientProvider>
    </SafeAreaView>
  );
};

const Home = () => {
  const {isLoading, list, refetch} = useList();
  const {mutate} = useUserData();
  useEffect(() => {
    console.log('list: ', list);
  }, [list]);

  return (
    <View style={styles.main}>
      <ActivityIndicator animating={isLoading} size={'large'} color={'#fff'} />
      <Button title={'Get data'} onPress={refetch} />
      <Button title={'Push data'} onPress={() => mutate()} />
    <View/>
  );
};

After importing the needed stuff from react-query, we create a new QueryClient in our entry file (usually, it's App.js / App.tsx). Then we surround our whole application with a provider allowing access to the data and initialized queries. Instead of Home, it can be the entire NavigationContainer or anything else.

Then, we move on to using this library directly in the component itself. In the above example, we used the useQuery Hook (one of the two most basic ones, we will use the second one later). Therefore, its initialization could be closed on this line of code.

const { isLoading, error, data } = useQuery( 'queryUID', getData);

However, in this situation, I wanted to have access to the refetch function, which allows downloading the data from the server again, to update the current state (a proper function, for example, when downloading lists). The call of such a query happens during component mounting, we do not have to run it manually using button or Hook useEffect.

Worth checking: Constate Library - alternative to global state of the app management

You must remember, however, that the declared query must have its unique ID throughout the application. Why? Mainly to avoid sending unnecessary requests during the re-rendering of a component. For example, imagine a situation where the component, for some reason, would render in 5 seconds.

If we didn't use react query and wanted to send a request in useEffect while the component was being rendered, it would be executed needlessly every 5 seconds. React Query, thanks to identifiers, remembers which request was executed and did not duplicate it. Of course, you can call it with the refetch function, but it is done intentionally, not accidentally.

Why does combining React Query with Constate give excellent possibilities?

The main difference between these two libraries is that React Query is a library for updating the server state to the application state. Constate manages the global application state on the user's device. So what does the combination of these two creations give us? Access to data from the server and the ability to edit it throughout the application without re-initializing the query, excellent code transparency, and a straightforward and understandable structure.

In addition, the fact that both of these libraries are based on Hooks gives us great consistency when writing code. In my opinion, it is much easier to understand than creating such a solution, even with the use of Redux. I had the opportunity to work with such a solution, and for a long time, it seemed very imprecise.

Also read: How to use TypeScript Record Utility Type? Detailed guide

Time for a small example of how to combine it

We must remember that Constate will use React Query, so when initializing providers in the entry file, QueryProvider will be the parent of ConstateProvider. Next, we need to create Hooks that will handle Constate and React Query. Below are two examples:

const useDataContext = () => {
  const [name, setName] = useState<string>('');
  const [age, setAge] = useState<string>('');
  const mutation = useMutation(() => pushData(name, age));
  const {isLoading, error, mutate} = mutation;

  useEffect(() => {
    error && console.warn(error);
  }, [error]);

  return {name, setName, age, setAge, isLoading, mutate};
};

export const [UserDataProvider, useUserData] = constate(useDataContext);

type TListObject = {
  name: string;
  age: string;
};

const useListContext = () => {
  const [list, setList] = useState<TListObject[]>([]);
  const query = useQuery('dataUID', getData);
  const refetch = async () => await query.refetch();
  const {data, isLoading, error} = query;

  useEffect(() => {
    data && setList([...data]);
  }, [data]);

  useEffect(() => {
    error && console.warn(error);
  }, [error]);

  return {list, isLoading, refetch};
};

export const [ListProvider, useList] = constate(useListContext);

Hooks defined in such a way can be used directly in the component, and we can easily access a lot of necessary information, such as error information and flags such as isLoading, which almost every time we need to write a separate useState to render the loader in the right moment and much more. In the end, we have a small example of calling the above Hooks in the code:

const Home = () => {
  const {isLoading, list, refetch} = useList();
  const {mutate} = useUserData();
  useEffect(() => {
    console.log('list: ', list);
  }, [list]);

  return (
    <View style={styles.main}>
      <ActivityIndicator animating={isLoading} size={'large'} color={'#fff'} />
      <Button title={'Get data'} onPress={refetch} />
      <Button title={'Push data'} onPress={() => mutate()} />
    </View>
  );
};

Summary

Today's post was just an introduction to using React Query. The possibilities of this library are much more significant, but those more interested will refer to the documentation. I hope that showing an alternative to managing the global state of the application with the handling of requests will give you another technology to consider when planning an upcoming project.

I strongly encourage you to familiarize yourself with the combination of the libraries mentioned above. Of course, this approach will not be perfect for every project, as it is always individual. However, I think it can be an exciting and optimal way to achieve the desired results in the project. Have fun!