Last active
September 18, 2024 13:06
-
-
Save toky-nomena/7d18638d1dd81eabbb7f825d9d79b4ef to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, useEffect, useTransition } from 'react'; | |
import { useQuery, useQueryClient, QueryFunction } from 'react-query'; | |
import axios, { AxiosError } from 'axios'; | |
import Select from 'react-select'; | |
interface Option { | |
value: string; | |
label: string; | |
} | |
interface OptionDetails { | |
id: string; | |
description: string; | |
} | |
const CACHE_NAME = 'api-cache'; | |
// Generic fetch function for GET requests with caching | |
const fetchFromApiWithCache = async <T,>( | |
url: string, | |
signal?: AbortSignal | |
): Promise<T> => { | |
const cache = await caches.open(CACHE_NAME); | |
const cachedResponse = await cache.match(url); | |
if (cachedResponse) { | |
return cachedResponse.json(); | |
} | |
const { data } = await axios.get<T>(url, { signal }); | |
const cacheResponse = new Response(JSON.stringify(data)); | |
await cache.put(url, cacheResponse); | |
return data; | |
}; | |
// Generic query function | |
const createQueryFunction = <T,>(baseUrl: string): QueryFunction<T, [string, string]> => { | |
return async ({ queryKey, signal }) => { | |
const [, id] = queryKey; | |
const url = `${baseUrl}/${id}`; | |
return fetchFromApiWithCache<T>(url, signal); | |
}; | |
}; | |
// Specific query function | |
const fetchOptionDetails = createQueryFunction<OptionDetails>('https://api.example.com/options'); | |
// Main component | |
const SelectWithDetails: React.FC = () => { | |
const [selectedOption, setSelectedOption] = useState<Option | null>(null); | |
const [isPending, startTransition] = useTransition(); | |
const [showDetails, setShowDetails] = useState(false); | |
const queryClient = useQueryClient(); | |
const { data: optionDetails, isLoading, error } = useQuery<OptionDetails, AxiosError>( | |
['optionDetails', selectedOption?.value ?? ''], | |
fetchOptionDetails, | |
{ | |
enabled: !!selectedOption, | |
staleTime: 5 * 60 * 1000, // 5 minutes | |
cacheTime: 10 * 60 * 1000, // 10 minutes | |
} | |
); | |
useEffect(() => { | |
return () => { | |
if (selectedOption) { | |
queryClient.cancelQueries(['optionDetails', selectedOption.value]); | |
} | |
}; | |
}, [selectedOption, queryClient]); | |
const handleOptionChange = (newOption: Option | null) => { | |
setSelectedOption(newOption); | |
// Use startTransition for low priority rendering of details | |
startTransition(() => { | |
setShowDetails(!!newOption); | |
}); | |
}; | |
const options: Option[] = [ | |
{ value: '1', label: 'Option 1' }, | |
{ value: '2', label: 'Option 2' }, | |
{ value: '3', label: 'Option 3' }, | |
]; | |
return ( | |
<div> | |
<Select | |
options={options} | |
value={selectedOption} | |
onChange={handleOptionChange} | |
isClearable | |
placeholder="Select an option" | |
/> | |
{isPending && <p>Loading...</p>} | |
{showDetails && ( | |
<React.Suspense fallback={<p>Loading details...</p>}> | |
<OptionDetailsDisplay | |
isLoading={isLoading} | |
error={error} | |
optionDetails={optionDetails} | |
/> | |
</React.Suspense> | |
)} | |
</div> | |
); | |
}; | |
// Separate component for option details | |
const OptionDetailsDisplay: React.FC<{ | |
isLoading: boolean; | |
error: AxiosError | null; | |
optionDetails: OptionDetails | undefined; | |
}> = ({ isLoading, error, optionDetails }) => { | |
if (isLoading) return <p>Loading option details...</p>; | |
if (error) return <p>Error: {error.message}</p>; | |
if (!optionDetails) return null; | |
return ( | |
<div> | |
<h3>Option Details:</h3> | |
<p>ID: {optionDetails.id}</p> | |
<p>Description: {optionDetails.description}</p> | |
</div> | |
); | |
}; | |
export default SelectWithDetails; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment