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
sleeppromise is cancelled - A new debounce timer starts
No explicit cleanup is needed because:
- The
AbortSignalis passed through to the underlyingfetchcall - When aborted, both the
sleeppromise 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
useQueryinstead ofuseTanStackQueryInstead- Use
useMutationinstead ofuseDeprecatedMutation
These exist only for backward compatibility and will be removed in future versions.