Data fetching in CARE
Introduction
CARE uses TanStack Query for data fetching and state management.
API Route Definitions
Routes are defined with their path, method, and types:
const routes = {
users: {
current: {
path: "/api/v1/users/getcurrentuser/",
method: "GET",
TRes: Type<UserModel>(), // Response type
}
},
}
Basic Usage with useQuery
→ TanStack Docs: useQuery Overview
import { useQuery } from "@tanstack/react-query";
import query from "@/Utils/request/query";
export default function UserProfile() {
const { data, isLoading } = useQuery({
queryKey: [routes.users.current.path],
queryFn: query(routes.users.current)
});
if (isLoading) return <Loading />;
return (
<div>
<h1>{data?.name}</h1>
<p>{data?.email}</p>
</div>
);
}
Passing Query Parameters
For URLs like /api/v1/medicine/search/?search=Paracetamol
:
function SearchMedicines() {
const { data } = useQuery({
queryKey: [routes.medicine.search.path, "Paracetamol"],
queryFn: query(routes.medicine.search, {
queryParams: { search: "Paracetamol" }
}),
enabled: true,
});
return <MedicinesList medicines={data?.results} />;
}
Using Path Parameters
→ TanStack Docs: Dynamic Query Keys
For URLs like /api/v1/consultation/123/prescriptions/
:
function PrescriptionsList({ consultationId }: { consultationId: string }) {
const { data } = useQuery({
queryKey: [routes.prescriptions.list.path, consultationId],
queryFn: query(routes.prescriptions.list, {
pathParams: { consultation_id: consultationId }
})
});
return <List items={data?.results} />;
}
Using Request Body
While useQuery
is typically used for GET requests, it can also handle POST requests that are semantically queries (like search operations):
function SearchPatients() {
const { data } = useQuery({
queryKey: ['patients', 'search', searchTerm],
queryFn: query(routes.patients.search, {
body: {
search_text: searchTerm,
filters: {
district: selectedDistrict,
status: "Active"
}
}
}),
enabled: Boolean(searchTerm)
});
return <PatientsList patients={data?.results} />;
}
Note: For mutations (creating, updating, or deleting data), use useMutation
instead.
Debounced Queries
For scenarios requiring debounced API calls (like search inputs), use query.debounced
. This helps reduce unnecessary API calls during rapid user input:
function SearchComponent() {
const [search, setSearch] = useState("");
const { data } = useQuery({
queryKey: ['search', search],
queryFn: query.debounced(routes.search, {
queryParams: { q: search },
debounceInterval: 500 // Optional: defaults to 500ms
}),
enabled: search.length > 0
});
return (
<Input
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search..."
/>
);
}
The debounced query will wait for the specified interval after the last call before executing the request. This is particularly useful for:
- Search inputs
- Auto-complete fields
- Any UI element where the user might trigger rapid successive updates
How Debounced Queries Work
→ TanStack Docs: Query Cancellation
The implementation leverages TanStack Query's built-in cancellation through AbortSignal
:
- When a new query is triggered, TanStack Query automatically creates an
AbortSignal
- If a new query starts before the debounce delay finishes:
- The previous signal is aborted automatically by TanStack Query
- The previous
sleep
promise is cancelled - A new debounce timer starts
No explicit cleanup is needed because:
- The
AbortSignal
is passed through to the underlyingfetch
call - When aborted, both the
sleep
promise and the fetch request are cancelled automatically - TanStack Query handles the abortion and cleanup of previous in-flight requests
Mutations
CARE provides a mutate
utility function that works seamlessly with TanStack Query's useMutation
hook for creating, updating, or deleting data:
import { useMutation } from "@tanstack/react-query";
import mutate from "@/Utils/request/mutate";
function CreatePrescription({ consultationId }: { consultationId: string }) {
const { mutate: createPrescription, isPending } = useMutation({
mutationFn: mutate(routes.prescriptions.create, {
pathParams: { consultationId },
}),
onSuccess: () => {
toast.success("Prescription created successfully");
},
});
return (
<Button
onClick={() => createPrescription({
medicineId: "123",
dosage: "1x daily"
})}
disabled={isPending}
>
Create Prescription
</Button>
);
}
Using Path Parameters with Mutations
For URLs that require path parameters, like /api/v1/patients/123/update/
:
function UpdatePatient({ patientId }: { patientId: string }) {
const { mutate: updatePatient } = useMutation({
mutationFn: mutate(routes.patients.update, {
pathParams: { id: patientId },
silent: true // Optional: suppress error notifications
})
});
const handleSubmit = (data: PatientData) => {
updatePatient(data);
};
return <PatientForm onSubmit={handleSubmit} />;
}
The mutate
utility accepts configuration options similar to the query
utility:
pathParams
: For URL parametersqueryParams
: For query string parameterssilent
: Optional boolean to suppress error notifications- Additional request options as needed
Further Reading
For advanced features like:
See the TanStack Query docs for complete documentation.
Legacy Hooks (Deprecated)
Note: The following hooks are deprecated:
- Use
useQuery
instead ofuseTanStackQueryInstead
- Use
useMutation
instead ofuseDeprecatedMutation
These exist only for backward compatibility and will be removed in future versions.