@concepta/react-data-provider
Overview
Rockets Data Access is a library for React applications that simplifies API interactions by integrating with Axios, managing authentication tokens and providing custom hooks for handling asynchronous requests and state. It enhances code maintainability and reliability with type safety, centralized configuration, and built-in token management, making it easier to handle HTTP requests and state management in a standardized way.
Important
This module only helps you to handle basic requests on your application. There is no magic, you are still responsible for providing correct midlewares to auth requests, handle responses and errors
Getting started
This package will expose the following
- The
ClientProvider
context that manages the base URL and error handling for token refresh operations, ensuring consistent configuration and error management across the application. - The
useDataProvider
hook that provides a set of functions (post, get, put, patch, delete) to simplify making authenticated HTTP requests and handling responses. - The
useQuery
hook to manage the state and lifecycle of asynchronous functions, handling loading, success, and error states automatically.
Important
- You have to provide api information to dataProvider make the requests correctly
- You have to provide utility funcions like getAccessToken, getNewToken (Refresh) so dataProvider can handle the requests correctly
Installation & config
First add the Data Provider package to your project. You can install it using npm or yarn:
npm install @concepta/react-data-provider
yarn add @concepta/react-data-provider
Using the ClientProvider
The ClientProvider
component is essential for setting up the context that the Data Provider uses to manage the base URL and handle token refresh errors. It ensures that your application has a consistent configuration for making HTTP requests. Here's how you can use it in your application:
Wrap Your App with ClientProvider
:
To use the ClientProvider
, wrap it around your application's root component. This is typically done in your index.tsx
or App.tsx
file. Provide the baseUrl
and onRefreshTokenError
props if needed.
import React from 'react';
import ReactDOM from 'react-dom';
import { ClientProvider } from '@concepta/react-data-provider';
import App from './App';
ReactDOM.render(
<ClientProvider
baseUrl="YOUR_API_URL"
onRefreshTokenError={(error) => {
console.error('Token refresh error:', error);
// Handle token refresh error, e.g., redirect to login
}}
>
<App />
</ClientProvider>,
document.getElementById('root'),
);
baseUrl
(optional): The base URL for all HTTP requests. This can be an environment variable instead.onRefreshTokenError
(optional): A callback function that handles errors occurring during the token refresh process.
BaseUrl in the .env file
If you don't provide a baseUrl
prop to your ClientProvider
, you have to add a variable to your .env file called NEXT_PUBLIC_API_URL.
.env
NEXT_PUBLIC_API_URL="[YOUR API BASE URL]
If you apply both methods, Data Provider will use the url provided for the baseUrl
prop in <ClientProvider>
API Request Methods and Parameters in useDataProvider
The useDataProvider
hook provides an easy way to make HTTP requests using various methods: POST
, GET
, PUT
, PATCH
, and DELETE
. Each method accepts specific parameters that help configure the request. Below is a detailed explanation of each method and its parameters.
Shared parameters
All of the requests accepts these shared parameters:
- uri
(string)
: The endpoint URI. - headers
(Record<string, string>, optional)
: Additional headers to include in the request. - queryParams
(Record<string, string | string[] | number | undefined>, optional)
: Query parameters to append to the URL. - body
(TRequestBody, optional)
: The request payload. - signal
(AbortSignal, optional)
: Signal to abort the request.
POST | PUT | PATCH Methods
- The
post
method is used to send data to the server. - The
put
method is used to update existing data on the server. - The
patch
method is used to partially update existing data on the server.
Besides the shared parameters, they also accept:
- body (TRequestBody, optional): The request payload.
The useQuery Hook
The useQuery
hook is designed to handle asynchronous operations and manage the corresponding state, including loading, success, and error states. This section details how to use the useQuery
hook, its parameters, and it's return.
useQuery
accepts two generic types: TQueryData
and TError
.
TQueryData & TError
TQueryData
is a generic type parameter that specifies the expected shape of the data returned by the asynchronous function.TError
is a generic type parameter that specifies the expected shape of the error object returned by the asynchronous function.
Example Usage
import React, { useEffect } from 'react';
import useDataProvider, { useQuery } from '@concepta/react-data-provider';
// Define the type for the user data
interface User {
id: number;
name: string;
email: string;
}
// Define the type for the error object
interface FetchError {
message: string;
code: number;
}
const UsersComponent = () => {
const { get } = useDataProvider();
// Define the async function using the "get" function from "useDataProvider"
const fetchUsers = get({ uri: '/users' }); // AsyncFunction
// Use useQuery hook with TQueryData specified as User[] and TError specified as FetchError
const { execute, status, isPending, data, error, refresh } = useQuery<
User[],
FetchError
>(fetchUsers, true, {
onError: (error) => console.error('Error fetching users:', error),
onSuccess: (data) => console.log('Fetched users:', data),
onFinish: (status) => console.log('Fetch finished with status:', status),
});
useEffect(() => {
execute();
}, [execute]);
return (
<div>
{isPending && <p>Loading...</p>}
{status === AsyncStatus.error && <p>Error: {error?.message}</p>}
{status === AsyncStatus.success && (
<ul>
{data?.map((user) => (
<li key={user.id}>
{user.name} - {user.email}
</li>
))}
</ul>
)}
<button onClick={refresh}>Refresh</button>
</div>
);
};
export default UsersComponent;
Parameters
The useQuery
hook accepts four parameters
- asyncFn (AsyncFunction): An asynchronous function that the
useQuery
hook will execute. This function returns a promise that resolves to the expected data type. - immediate (boolean): A boolean indicating whether the function should be executed immediately when the hook is called. Defaults to
false
. - options (object): An object containing various callback functions and data formatting options (more details below).
- args (boolean): Optional arguments to be passed to the asynchronous function when it is executed.
The Options parameters:
onError
A callback function that is called if the asynchronous function throws an error.
onError: (error) => {
console.error('An error occurred:', error);
};
onSuccess
A callback function that is called when the asynchronous function successfully completes.
onSuccess: (data) => {
console.log('Data fetched successfully:', data);
};
onFinish
A callback function that is called when the asynchronous operation finishes, regardless of whether it was successful or resulted in an error.
onFinish: (status) => {
console.log('Operation finished with status:', status);
};
formatData
A function that formats the data returned by the asynchronous function before it is set in the state.
formatData: (data) => {
return data.map((item) => ({
...item,
formatted: true,
}));
};
useQuery
return
The useQuery
hook returns the following:
-
execute: A function that can be called to manually trigger the asynchronous function.
-
status: The current status of the asynchronous operation. Can be
idle
,pending
,success
, orerror
. -
isPending: A boolean indicating whether the asynchronous operation is currently in progress.
-
data: The data returned by the asynchronous function, if the operation was successful.
-
error: The error thrown by the asynchronous function, if any.
-
refresh: A function that can be called to re-execute the asynchronous function with the same arguments.
Example Usage
Simple post with useDataProvider
import useDataProvider from '@concepta/react-data-provider';
const MyComponent = () => {
const { post } = useDataProvider();
const handleClick = () => {
post({
uri: '/todo-list',
body: { text: 'Buy tomatoes' },
});
};
return (
<div>
<Button variant="contained" onClick={handleClick}>
Add tomatoes
</Button>
</div>
);
};
Get request with useQuery
import { useDataProvider } from '@concepta/react-data-provider';
interface User {
id: number;
name: string;
email: string;
}
const MyComponent = () => {
const { get } = useDataProvider();
const fetchUsers = () =>
get({
uri: `/user-list`,
});
const { execute, status, isPending, data, error, refresh } = useQuery<User[]>(
fetchUsers,
false, // immediate = false
{
onError: (error) => console.error('Error fetching users:', error),
onSuccess: (data) => console.log('Fetched users:', data),
onFinish: (status) => console.log('Fetch finished with status:', status),
},
);
return (
<div>
My Component
<button onClick={execute}>Get list</button>
</div>
);
};