133 lines
3.3 KiB
TypeScript
133 lines
3.3 KiB
TypeScript
import ReactDOM from 'react-dom/client'
|
|
import {
|
|
QueryClient,
|
|
QueryClientProvider,
|
|
queryOptions,
|
|
useQueries,
|
|
} from '@tanstack/react-query'
|
|
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
|
import { useState } from 'react'
|
|
import { loadCharacter } from './character-loader'
|
|
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: {
|
|
retry: 0,
|
|
},
|
|
},
|
|
})
|
|
|
|
const characterQueryOptions = (id: number) =>
|
|
queryOptions({
|
|
queryKey: ['character', id],
|
|
queryFn: () => loadCharacter(id),
|
|
staleTime: 60 * 1000,
|
|
})
|
|
|
|
function Characters({ ids }: { ids: Array<number> }) {
|
|
const characters = useQueries({
|
|
queries: ids.map((id) => characterQueryOptions(id)),
|
|
})
|
|
|
|
return characters.map((character, index) => {
|
|
if (character.status === 'pending') {
|
|
return 'Loading...'
|
|
}
|
|
if (character.status === 'error') {
|
|
return 'Error...'
|
|
}
|
|
if (character.data === null) {
|
|
return 'Not found ...'
|
|
}
|
|
|
|
return (
|
|
<div key={String(index)} style={{ display: 'flex' }}>
|
|
<img
|
|
width={100}
|
|
height={100}
|
|
alt={character.data.image}
|
|
src={character.data.image}
|
|
/>
|
|
<div style={{ paddingLeft: 10, textAlign: 'left' }}>
|
|
<h5 style={{ marginTop: 0, marginBottom: 5 }}>
|
|
{character.data.name} (#{character.data.id})
|
|
</h5>
|
|
<p style={{ marginTop: 0, marginBottom: 2 }}>
|
|
Status: {character.status}
|
|
</p>
|
|
<p style={{ marginTop: 0, marginBottom: 2 }}>
|
|
Species: {character.data.species}
|
|
</p>
|
|
<p style={{ marginTop: 0, marginBottom: 2 }}>
|
|
Gender: {character.data.gender}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
})
|
|
}
|
|
|
|
function App() {
|
|
const [ids, setIds] = useState<Array<number>>([])
|
|
const [idsToLoad, setIdsToLoad] = useState<Array<number>>([])
|
|
const [error, setError] = useState('')
|
|
|
|
return (
|
|
<div className="App">
|
|
<h2>Character fetcher</h2>
|
|
<div>
|
|
<label style={{ display: 'block' }}>
|
|
Character ids (numberic, comma seprated)
|
|
</label>
|
|
<input
|
|
style={{ display: 'inline-block' }}
|
|
placeholder="ids"
|
|
onChange={(event) => {
|
|
const text = event.target.value.replace(' ', '')
|
|
const ids = text.split(',').map((id) => parseInt(id, 10))
|
|
const allValid = ids.reduce(
|
|
(valid, id) => valid && !isNaN(id),
|
|
true,
|
|
)
|
|
|
|
if (allValid) {
|
|
setIds(ids)
|
|
setError('')
|
|
} else {
|
|
setError('Invalid.. only use numbers and comma')
|
|
}
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
{error ? <h3 style={{ color: 'red' }}>{error}</h3> : null}
|
|
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
setIdsToLoad(ids)
|
|
}}
|
|
>
|
|
Load characters
|
|
</button>
|
|
<div>
|
|
<h4 style={{ textAlign: 'left' }}>Characters:</h4>
|
|
<Characters ids={idsToLoad} />
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function Root() {
|
|
return (
|
|
<QueryClientProvider client={queryClient}>
|
|
<App />
|
|
<ReactQueryDevtools initialIsOpen />
|
|
</QueryClientProvider>
|
|
)
|
|
}
|
|
|
|
const rootElement = document.getElementById('root')
|
|
if (!rootElement) throw new Error('Missing #root element')
|
|
ReactDOM.createRoot(rootElement).render(<Root />)
|