0.5.0 • Published 1 month ago

@ventlio/tanstack-query v0.5.0

Weekly downloads
-
License
MIT
Repository
github
Last release
1 month ago

This is a complementary library that should be used with @tanstack/react-query for API REQUESTS and more

WHY THIS PACKAGE?

We have been working on a project using ReactJS and React-Native and we were happy with the React-Query package, now known as Tanstack-Query, which had made our lives easier by managing most of the state management features needed for enterprise software development. This had helped us focus on building functionalities easily without worrying about server state management.

However, we still faced some challenges. For each project, we have to configure how Tanstack-Query would connect to the Backend API and manage GET, POST, PUT, and PATCH requests efficiently. One of the major challenges was handling dynamic queryKeys in Tanstack-Query. For example, when building a screen with pagination, useQuery will have re-call the queryFunction every time the key changed, which will load the current page data. This meant that the queryKey changed dynamically, and sometimes we needed to use the queryKey of the current page to perform certain tasks like updating a specific row in the view. This was always tedious.

But we were not discouraged. So, we set out to find a solution which led to the development of this package. This package would handle dynamic queryKeys in Tanstack-Query and solve other related problems. It made configuring Tanstack-Query to connect to the Backend API and managing GET, POST, PUT, and PATCH requests a breeze. It also solved the problem of dynamic queryKeys, making it much easier to update specific rows in the view.


Please note that this package is still being developed and may not function as expected. We are working to refine its implementation structure to meet a functional standard. The documentation may not align with the current implementation, so if you encounter any difficulties while setting up the package, please raise an issue in the GitHub repository. We appreciate your patience and understanding as we work to improve this package.

Tasks

  • Global settings for requests
  • Requests context implementations
  • Post, Get, Patch Requests
  • Query key tracker to track dynamic query and help fetch query cache from any page
  • Persistent queries implementation (Not completed)
  • Put request
  • Generic return type (this is currently an issue if the API does not return object with the necessary properties required by the library)
  • Generic Pagination for any response without infinite queries
  • Infinite Get Query implementation (still using implementation meant for our use case)
  • Server sent events
  • Socket implementations
  • Tests

Installation

You must install @tanstack/react-query and axios first to use this package

yarn add @tanstack/react-query axios

After that install this package

$ npm install @ventlio/tanstack-query

OR

$ yarn add @ventlio/tanstack-query

CURRENT RETURN TYPE

Currently the library return type expects data structure of the below schema, so depending on the API design, you can reach out to the developer to implement the return type that follows the below schema.

export interface IRequestError {
  statusCode: number;
  message: string;
  timeStamp: Date;
  status: boolean;
  data?: any;
}

export interface IRequestSuccess<T> {
  statusCode: number;
  message: string;
  timeStamp: Date;
  status: boolean;
  data: T;
}

Getting Started

Follow the below instructions to have the package running on your project

Set the environment variables

# For ReactJS
REACT_APP_API_URL='https://api.example.com'
REACT_APP_API_TIMEOUT=300000

# For NextJS
NEXT_PUBLIC_API_URL='https://api.example.com'
NEXT_PUBLIC_API_TIMEOUT=300000
import { QueryClient } from '@tanstack/react-query';
import { TanstackQueryConfig, bootstrapQueryRequest } from '@ventlio/tanstack-query';

// Global queryClient
const queryClient = new QueryClient();

// do this before adding the queryClient to QueryClientProvider
bootstrapQueryRequest(queryClient);

// recommended setup for mobile apps as the .env setup won't work
bootstrapQueryRequest(queryClient, {
  context: 'app', // this is required to make the library switch to app context where necessary
  environments: {
    appBaseUrl: baseUrl,
    appTimeout: 30000,
  },
  modelConfig: {
    idColumn: 'id', // used for useQueryModel to uniquely identify query data in a collection/array instance
  },
});

You can now use it in a QueryClientProvider

import { QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from './queryClient';

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <HomePage />
      <About />
    </QueryClientProvider>
  );
}

Updating the configurations inside a component

import { useQueryBaseURL, useQueryHeaders, useQueryTimeout } from '@ventlio/tanstack-query';

function LoginPage() {
  const { headers, setQueryHeaders } = useQueryHeaders();
  const [authToken, setAuthToken] = useState();
  useEffect(() => {
    // after user has logged in successfully set the authorization header token
    // this should also be done mostly in the layout that contains the authenticated views of the app
    // for instance in AuthLayout, so that after login in the authToken can still be used to authenticate future request
    // when user refreshes the page
    setQueryHeaders({ Authorization: `Bearer ${authToken}` });
  }, []);

  return <>{/** codes */}</>;
}

Hooks

useGetRequest Hook

The useGetRequest hook is a custom React Query hook that handles GET requests. It returns an object that contains the current state of the query, as well as several methods to update the query.

Parameters

The useGetRequest hook takes an object as its parameter with the following properties:

  • path: a string representing the URL path for the GET request. Required.
  • load: a boolean indicating whether to load the query immediately. Default: false.
  • queryOptions: an object containing additional options for the query. Optional.
  • keyTracker: a string that tracks changes to the query key. Optional.

Return Value

The useGetRequest hook returns an object with the following properties:

  • data: the data returned by the query, if successful.
  • isLoading: a boolean indicating whether the query is currently loading.
  • isError: a boolean indicating whether the query resulted in an error.
  • error: the error message, if an error occurred.
  • updatePath: a function that updates the path of the query.
  • nextPage: a function that navigates to the next page of results, if pagination is present.
  • prevPage: a function that navigates to the previous page of results, if pagination is present.
  • get: a function that updates the path and options of the query and returns the data.
  • gotoPage: a function that navigates to a specific page of results, if pagination is present.
  • page: the current page number, if pagination is present.
  • queryKey: an array representing the query key.

Example Usage

import { useGetRequest } from '@ventlio/tanstack-query';

const MyComponent = () => {
  const { data, isLoading, isError, error, updatePath, nextPage, prevPage, get, gotoPage, page, queryKey } =
    useGetRequest({
      path: '/api/mydata',
      load: true,
      queryOptions: {
        staleTime: 10000,
        refetchOnWindowFocus: false,
      },
      keyTracker: 'mydata',
    });

  return (
    <div>
      {isLoading && <p>Loading...</p>}
      {isError && <p>{error.message}</p>}
      {data && (
        <ul>
          {data.map((item) => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
      )}
      <button onClick={() => nextPage()}>Next Page</button>
      <button onClick={() => prevPage()}>Previous Page</button>
      <button onClick={() => gotoPage(1)}>Go to Page 1</button>
      <button onClick={() => get('/api/mydata?page=2')}>Get Page 2</button>
    </div>
  );
};

usePostRequest Hook

The usePostRequest function is a custom React hook that provides an easy way to make POST requests using the @tanstack/react-query library.

Usage

To use the usePostRequest hook, import it from the module where it's defined and call it in your component like this:

import { usePostRequest } from '@ventlio/tanstack-query';

const MyComponent = () => {
  const { post, isLoading, isError, isSuccess, data, error } = usePostRequest({
    path: '/api/posts',
    isFormData: true,
  });

  const handleFormSubmit = async (formData) => {
    try {
      const response = await post(formData);
      console.log(response);
    } catch (e) {
      console.error(e);
    }
  };

  return (
    <form onSubmit={handleFormSubmit}>
      {/* form inputs */}
      <button type="submit">Submit</button>
    </form>
  );
};

Parameters

The usePostRequest hook takes an object with two optional properties:

  • path (required): the URL path to make the POST request to.
  • isFormData (optional, default is false): a boolean that indicates whether to send the request data as form data.

Returns

The usePostRequest hook returns an object with the following properties:

  • post: a function that takes the data to be sent in the request and an optional MutateOptions object, and returns a promise that resolves to the response data.
  • isLoading: a boolean that indicates whether the request is currently loading.
  • isError: a boolean that indicates whether the request resulted in an error.
  • isSuccess: a boolean that indicates whether the request was successful.
  • data: the response data, if the request was successful.
  • error: the error object, if the request resulted in an error.

Implementation Details

The usePostRequest hook internally uses the useMutation hook from the @tanstack/react-query library to register a mutation that sends the POST request. It also uses the internal makeRequest function to actually make the request.

It users the useQueryConfig hook internally to get the headers, baseURL, and timeout values that are passed to makeRequest.

If the request is successful, the hook scrolls the page to the top using the window.scrollTo method. If the request fails, it also scrolls to the top before throwing the error.

usePatchRequest Hook

usePatchRequest is a React hook that allows you to make PATCH requests. It takes in a path parameter that specifies the path of the API endpoint to call.

usePatchRequest<TResponse>({ path: string }): { patch, ...mutation }

Parameters

  • path (required): A string that represents the path of the API endpoint to call.

Return Values

usePatchRequest returns an object with the following properties:

  • patch: A function that can be called to initiate a PATCH request.
  • ...mutation: The rest of the properties returned by the useMutation hook.

Example

import { usePatchRequest } from '@ventlio/tanstack-query';

function App() {
  const { patch, isLoading, isError, isSuccess, data } =
    usePatchRequest <
    User >
    {
      path: '/users/1',
    };

  const updateUser = async (user: User) => {
    await patch(user);
  };

  return (
    <div>
      <button onClick={() => updateUser({ name: 'John' })}>Update User</button>
      {isLoading && <div>Loading...</div>}
      {isError && <div>Error updating user</div>}
      {isSuccess && <div>Successfully updated user {data?.name}</div>}
    </div>
  );
}

In this example, we are using the usePatchRequest hook to send a PATCH request to update a user's name. We call the patch function with the new user data to initiate the request. The isLoading, isError, isSuccess, and data properties are used to display the request status and response data.

Note that we have assumed the existence of a User interface in this example.

useDeleteRequest Hook

The useDeleteRequest hook is a custom hook used to make HTTP DELETE requests using @tanstack/react-query library. This hook returns an object that contains a destroy function and other properties inherited from the useQuery hook.

Parameters

This hook does not take any parameter.

Return value

The hook returns an object containing:

  • destroy: a function used to make the DELETE request and returns the server's response.
  • Other properties inherited from the useQuery hook.

Example

Here's an example of how to use the useDeleteRequest hook:

import { useDeleteRequest } from '@ventlio/tanstack-query';

function DeleteButton({ link }) {
  const { isLoading, isError, error, data, destroy } = useDeleteRequest();

  const handleDelete = async () => {
    const response = await destroy(link);
    // do something with the response
  };

  return (
    <button onClick={handleDelete} disabled={isLoading}>
      {isLoading ? 'Deleting...' : 'Delete'}
    </button>
  );
}

In the above example, we created a DeleteButton component that uses the useDeleteRequest hook to make a DELETE request to the specified link when the button is clicked. The destroy function returns the server's response, which we can then use to update the UI.

useRefetchQuery

A simple utility function that utilizes useQueryClient hook from @tanstack/react-query to refetch a query and retrieve updated data.

Usage

  1. Import useRefetchQuery from your desired file:
import { useRefetchQuery } from '@ventlio/tanstack-query';
  1. Call useRefetchQuery with a queryKey parameter which is an array of any types that uniquely identifies the query:
const { refetchQuery } = useRefetchQuery(['myQueryKey']);
  1. Invoke refetchQuery function to refetch the query and retrieve updated data:
const { data } = await refetchQuery<MyDataType>();

If you want to refetch a different query, you can pass a different queryKey parameter to refetchQuery function:

const { data } = (await refetchQuery) < MyDataType > ['myOtherQueryKey'];

If you want to perform additional operations after refetching the query, you can use the data returned by refetchQuery function:

const { data } = await refetchQuery<MyDataType>();
// Perform additional operations with data

Parameters

  • queryKey: An array of any types that uniquely identifies the query.

Return Value

  • refetchQuery: A function that refetches the query and retrieves updated data.

Example

import { useQueryClient } from '@tanstack/react-query';
import { useRefetchQuery } from '@ventlio/tanstack-query';

const MyComponent = () => {
  const queryClient = useQueryClient();

  const { refetchQuery } = useRefetchQuery(['myQueryKey']);

  const handleClick = async () => {
    try {
      // Refetch the query and retrieve updated data
      const { data } = await refetchQuery<MyDataType>();

      // Perform additional operations with data
      console.log(data);
    } catch (error) {
      // Handle error
      console.error(error);
    }
  };

  return (
    <button onClick={handleClick}>
      Refetch Query
    </button>
  );
};

useKeyTrackerModel

A custom hook that utilizes useQueryClient hook from @tanstack/react-query and useState hook from react to track a query key and retrieve query data.

Usage

  1. Import useKeyTrackerModel from @ventlio/tanstack-query:
import { useKeyTrackerModel } from '@ventlio/tanstack-query';
  1. Call useKeyTrackerModel with a keyTracker parameter which is a string that uniquely identifies the query key:
const { refetchQuery, getQueryKey, queryKey, data } = useKeyTrackerModel < MyDataType > 'myKeyTracker';
  1. Invoke getQueryKey function to retrieve the query key:
const key = getQueryKey();
  1. Invoke refetchQuery function to retrieve query data:
const queryData = refetchQuery();
  1. Use queryKey and data as needed in your component:
return (
  <div>
    <p>Query Key: {queryKey}</p>
    <p>Query Data: {data}</p>
  </div>
);

Parameters

  • keyTracker: A string that uniquely identifies the query key.

Return Value

  • refetchQuery: A function that retrieves query data.
  • getQueryKey: A function that retrieves the query key.
  • queryKey: The query key.
  • data: The query data.

Example

import { useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';
import { useKeyTrackerModel } from '@ventlio/tanstack-query';

const MyComponent = () => {
  const queryClient = useQueryClient();

  const { refetchQuery, getQueryKey, queryKey, data } = useKeyTrackerModel < MyDataType > 'myKeyTracker';

  const handleClick = async () => {
    // Retrieve the query key
    const key = getQueryKey();

    // Retrieve query data
    const queryData = refetchQuery();

    // Perform additional operations with query key and data
    console.log(key, queryData);
  };

  return (
    <div>
      <button onClick={handleClick}>Get Query Data</button>
      <p>Query Key: {queryKey}</p>
      <p>Query Data: {data}</p>
    </div>
  );
};

Contributing

Contributions to this codebase are welcome. If you find any issues or have any suggestions, please feel free to create an issue or pull request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

0.5.0

1 month ago

0.3.6

2 months ago

0.3.8

2 months ago

0.3.7

2 months ago

0.3.9

2 months ago

0.3.5

5 months ago

0.3.4

5 months ago

0.3.0

6 months ago

0.3.2

6 months ago

0.3.1

6 months ago

0.3.3

6 months ago

0.2.85

6 months ago

0.2.84

7 months ago

0.2.83

7 months ago

0.2.82

7 months ago

0.2.81

7 months ago

0.2.7-2.2

8 months ago

0.2.80

7 months ago

0.2.7-2.1

8 months ago

0.2.89

6 months ago

0.2.87

6 months ago

0.2.86

6 months ago

0.2.74

8 months ago

0.2.73

8 months ago

0.2.72

8 months ago

0.2.71

8 months ago

0.2.70

8 months ago

0.2.79

7 months ago

0.2.78

8 months ago

0.2.77

8 months ago

0.2.76

8 months ago

0.2.75

8 months ago

0.2.69

8 months ago

0.2.66

11 months ago

0.2.63

1 year ago

0.2.62

1 year ago

0.2.61

1 year ago

0.2.60

1 year ago

0.2.65

11 months ago

0.2.64

11 months ago

0.2.63-beta.6.2

11 months ago

0.2.63-beta.1

12 months ago

0.2.59

1 year ago

0.2.63-beta.3

12 months ago

0.2.58

1 year ago

0.2.63-beta.2

12 months ago

0.2.57

1 year ago

0.2.63-beta.5

11 months ago

0.2.56

1 year ago

0.2.63-beta.4

12 months ago

0.2.55

1 year ago

0.2.63-beta.7

11 months ago

0.2.54

1 year ago

0.2.63-beta.6

11 months ago

0.2.63-beta.5.1

11 months ago

0.2.63-beta.5.2

11 months ago

0.2.53

1 year ago

0.2.52

1 year ago

0.2.51

1 year ago

0.2.50

1 year ago

0.2.49

1 year ago

0.2.48

1 year ago

0.2.47

1 year ago

0.2.46

1 year ago

0.2.45

1 year ago

0.2.44

1 year ago

0.2.43

1 year ago

0.2.42

1 year ago

0.2.41

1 year ago

0.2.40

1 year ago

0.2.39

1 year ago

0.2.38

1 year ago

0.2.37

1 year ago

0.2.36

1 year ago

0.2.35

1 year ago

0.2.34

1 year ago

0.2.33

1 year ago

0.2.32

1 year ago

0.2.31

1 year ago

0.2.30

1 year ago

0.2.29

1 year ago

0.2.28

1 year ago

0.2.27

1 year ago

0.2.26

1 year ago

0.2.25

1 year ago

0.2.24

1 year ago

0.2.23

1 year ago

0.2.22

1 year ago

0.2.21

1 year ago

0.2.20

1 year ago

0.2.19

1 year ago

0.2.18

1 year ago

0.2.17

1 year ago

0.2.16

1 year ago

0.2.15

1 year ago

0.2.14

1 year ago

0.2.13

1 year ago

0.2.12

1 year ago

0.2.11

1 year ago

0.2.10

1 year ago

0.2.9

1 year ago

0.2.8

1 year ago

0.2.7

1 year ago

0.2.6

1 year ago

0.2.5

1 year ago

0.2.4

1 year ago

0.2.3

1 year ago

0.2.2

1 year ago

0.2.1

1 year ago

0.2.0

1 year ago

0.1.13

1 year ago

0.1.12

1 year ago

0.1.11

1 year ago

0.1.10

1 year ago

0.1.9

1 year ago

0.1.8

1 year ago

0.1.7

1 year ago

0.1.6

1 year ago

0.1.5

1 year ago

0.1.4

1 year ago

0.1.3

1 year ago

0.1.2

1 year ago

0.1.1

1 year ago

0.1.0

1 year ago