snapshot of https://github.com/TanStack/query.git (main, 2026-09-16; blobs > 131072B omitted)
This commit is contained in:
19
examples/react/star-wars/src/App.tsx
Normal file
19
examples/react/star-wars/src/App.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { BrowserRouter as Router } from 'react-router-dom'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
||||
import Layout from './Layout'
|
||||
|
||||
import './styles.css'
|
||||
|
||||
const queryClient = new QueryClient()
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Router>
|
||||
<Layout />
|
||||
<ReactQueryDevtools initialIsOpen />
|
||||
</Router>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
82
examples/react/star-wars/src/Character.tsx
Normal file
82
examples/react/star-wars/src/Character.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import { useParams, Link as RouterLink } from 'react-router-dom'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { getCharacter, getFilm, getPlanet } from './api'
|
||||
|
||||
export default function Character() {
|
||||
let params = useParams()
|
||||
const characterId = params.characterId!
|
||||
|
||||
const { status, data } = useQuery({
|
||||
queryKey: ['character', characterId],
|
||||
queryFn: () => getCharacter(characterId),
|
||||
})
|
||||
|
||||
if (status === 'pending') return <p>Loading...</p>
|
||||
if (status === 'error') return <p>Error :(</p>
|
||||
|
||||
const homeworldUrlParts = data.homeworld.split('/').filter(Boolean)
|
||||
const homeworldId = homeworldUrlParts[homeworldUrlParts.length - 1]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-4xl">{data.name}</h2>
|
||||
<p>
|
||||
<strong>Born</strong>: {data.birth_year}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Eyes</strong>: {data.eye_color}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Hair</strong>: {data.hair_color}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Height</strong>: {data.height}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Mass</strong>: {data.mass}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Homeworld</strong>: <Homeworld homeworldId={homeworldId} />
|
||||
</p>
|
||||
|
||||
<h4 className="text-2xl pt-4">Films</h4>
|
||||
{data.films.map((film: any) => {
|
||||
const filmUrlParts = film.split('/').filter(Boolean)
|
||||
const filmId = filmUrlParts[filmUrlParts.length - 1]
|
||||
return <Film filmId={filmId} key={`${filmId}`} />
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Film({ filmId }: { filmId: string }) {
|
||||
const { data, status } = useQuery({
|
||||
queryKey: ['film', filmId],
|
||||
queryFn: () => getFilm(filmId),
|
||||
})
|
||||
|
||||
if (status === 'success') {
|
||||
return (
|
||||
<RouterLink
|
||||
className="text-blue-500 hover:underline"
|
||||
to={`/films/${filmId}`}
|
||||
>
|
||||
<h6 className="text-lg">
|
||||
{data.episode_id}. {data.title}
|
||||
</h6>
|
||||
</RouterLink>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function Homeworld({ homeworldId }: { homeworldId: string }) {
|
||||
const { data, status } = useQuery({
|
||||
queryKey: ['homeworld', homeworldId],
|
||||
queryFn: () => getPlanet(homeworldId),
|
||||
})
|
||||
|
||||
if (status === 'pending') return <span>Loading...</span>
|
||||
if (status === 'error') return <span>Error :(</span>
|
||||
|
||||
return <span>{data.name}</span>
|
||||
}
|
||||
33
examples/react/star-wars/src/Characters.tsx
Normal file
33
examples/react/star-wars/src/Characters.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Link as RouterLink } from 'react-router-dom'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { getCharacters } from './api'
|
||||
|
||||
export default function Characters() {
|
||||
const { status, data } = useQuery({
|
||||
queryKey: ['characters'],
|
||||
queryFn: () => getCharacters(),
|
||||
})
|
||||
|
||||
if (status === 'pending') return <p>Loading...</p>
|
||||
if (status === 'error') return <p>Error :(</p>
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-4xl">Characters</h2>
|
||||
{data.results.map((person: any) => {
|
||||
const personUrlParts = person.url.split('/').filter(Boolean)
|
||||
const personId = personUrlParts[personUrlParts.length - 1]
|
||||
return (
|
||||
<article key={personId}>
|
||||
<RouterLink
|
||||
className="text-blue-500 hover:underline"
|
||||
to={`/characters/${personId}`}
|
||||
>
|
||||
<h6 className="text-xl">{person.name}</h6>
|
||||
</RouterLink>
|
||||
</article>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
54
examples/react/star-wars/src/Film.tsx
Normal file
54
examples/react/star-wars/src/Film.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { useParams, Link as RouterLink } from 'react-router-dom'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { getFilm, getCharacter } from './api'
|
||||
|
||||
export default function Film() {
|
||||
const { filmId } = useParams()
|
||||
|
||||
if (!filmId) {
|
||||
return <p>Invalid film ID</p>
|
||||
}
|
||||
|
||||
const { data, status } = useQuery({
|
||||
queryKey: ['film', filmId],
|
||||
queryFn: () => getFilm(filmId),
|
||||
})
|
||||
|
||||
if (status === 'pending') return <p>Loading...</p>
|
||||
|
||||
if (status === 'error') return <p>Error :(</p>
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-4xl">{data.title}</h2>
|
||||
<p>{data.opening_crawl}</p>
|
||||
<br />
|
||||
<h4 className="text-2xl">Characters</h4>
|
||||
{data.characters.map((character: string) => {
|
||||
const characterUrlParts = character.split('/').filter(Boolean)
|
||||
const characterId = characterUrlParts[characterUrlParts.length - 1]
|
||||
return <Character characterId={characterId} key={characterId} />
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Character({ characterId }: { characterId: string }) {
|
||||
const { data, status } = useQuery({
|
||||
queryKey: ['character', characterId],
|
||||
queryFn: () => getCharacter(characterId),
|
||||
})
|
||||
|
||||
if (status !== 'success') {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<RouterLink
|
||||
className="text-blue-500 hover:underline"
|
||||
to={`/characters/${characterId}`}
|
||||
>
|
||||
<h6 className="text-lg">{data.name}</h6>
|
||||
</RouterLink>
|
||||
)
|
||||
}
|
||||
42
examples/react/star-wars/src/Films.tsx
Normal file
42
examples/react/star-wars/src/Films.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Link as RouterLink } from 'react-router-dom'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { getFilms } from './api'
|
||||
|
||||
export default function Films() {
|
||||
const { data, status } = useQuery({
|
||||
queryKey: ['films'],
|
||||
queryFn: () => getFilms(),
|
||||
})
|
||||
|
||||
if (status === 'pending') {
|
||||
return <p>Loading...</p>
|
||||
}
|
||||
if (status === 'error') {
|
||||
return <p>Error :(</p>
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-4xl">Films</h2>
|
||||
{data.results.map((film: any) => {
|
||||
const filmUrlParts = film.url.split('/').filter(Boolean)
|
||||
const filmId = filmUrlParts[filmUrlParts.length - 1]
|
||||
return (
|
||||
<article key={filmId}>
|
||||
<RouterLink
|
||||
className="text-blue-500 hover:underline"
|
||||
to={`/films/${filmId}`}
|
||||
>
|
||||
<h6 className="text-xl">
|
||||
{film.episode_id}. {film.title}{' '}
|
||||
<em>
|
||||
({new Date(Date.parse(film.release_date)).getFullYear()})
|
||||
</em>
|
||||
</h6>
|
||||
</RouterLink>
|
||||
</article>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
46
examples/react/star-wars/src/Home.tsx
Normal file
46
examples/react/star-wars/src/Home.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Link as RouterLink } from 'react-router-dom'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-4xl">TanStack Query Demo</h2>
|
||||
<p>Using the Star Wars API</p>
|
||||
<p>
|
||||
(Built by{' '}
|
||||
<a
|
||||
className="text-blue-500 hover:underline"
|
||||
href="https://twitter.com/Brent_m_Clark"
|
||||
>
|
||||
@Brent_m_Clark
|
||||
</a>
|
||||
)
|
||||
</p>
|
||||
<h5 className="text-2xl pt-4">Why TanStack Query?</h5>
|
||||
<p>
|
||||
In this demo you will be able to see how TanStack Query is a significant
|
||||
improvement over <strong>redux</strong>, <strong>mobx</strong>, and any
|
||||
other general-purpose state container.
|
||||
</p>
|
||||
<p>
|
||||
No reducers, thunks, or sagas. No ES6 models to maintain in order to tag
|
||||
them as observable.
|
||||
</p>
|
||||
<p>
|
||||
Simply associate a key with your fetch call and let{' '}
|
||||
<strong>TanStack Query</strong> handle the rest.
|
||||
</p>
|
||||
<h5 className="text-2xl pt-4">Ready to get started?</h5>
|
||||
<p>
|
||||
Check out the{' '}
|
||||
<RouterLink className="text-blue-500 hover:underline" to="/films">
|
||||
Films
|
||||
</RouterLink>{' '}
|
||||
and{' '}
|
||||
<RouterLink className="text-blue-500 hover:underline" to="/characters">
|
||||
Characters
|
||||
</RouterLink>
|
||||
!
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
37
examples/react/star-wars/src/Layout.tsx
Normal file
37
examples/react/star-wars/src/Layout.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Route, Link as RouterLink, Routes } from 'react-router-dom'
|
||||
import Films from './Films'
|
||||
import Film from './Film'
|
||||
import Characters from './Characters'
|
||||
import Character from './Character'
|
||||
import Home from './Home'
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<div>
|
||||
<nav className="bg-gray-300 w-full flex flex-row gap-6 justify-center items-center h-12">
|
||||
<RouterLink to="/">
|
||||
<span className="uppercase hover:underline">Home</span>
|
||||
</RouterLink>
|
||||
<RouterLink to="/films">
|
||||
<span className="uppercase hover:underline">Films</span>
|
||||
</RouterLink>
|
||||
<RouterLink to="/characters">
|
||||
<span className="uppercase hover:underline">Characters</span>
|
||||
</RouterLink>
|
||||
</nav>
|
||||
<div className="p-2">
|
||||
<Routes>
|
||||
<Route exact path="/films" element={<Films />} />
|
||||
<Route exact path="/films/:filmId" element={<Film />} />
|
||||
<Route exact path="/characters" element={<Characters />} />
|
||||
<Route
|
||||
exact
|
||||
path="/characters/:characterId"
|
||||
element={<Character />}
|
||||
/>
|
||||
<Route path="/" element={<Home />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
24
examples/react/star-wars/src/api.ts
Normal file
24
examples/react/star-wars/src/api.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
export const getFilms = async () => {
|
||||
const res = await fetch('https://swapi.dev/api/films/')
|
||||
return await res.json()
|
||||
}
|
||||
|
||||
export const getFilm = async (filmId: string) => {
|
||||
const res = await fetch(`https://swapi.dev/api/films/${filmId}/`)
|
||||
return await res.json()
|
||||
}
|
||||
|
||||
export const getCharacters = async () => {
|
||||
const res = await fetch('https://swapi.dev/api/people/')
|
||||
return await res.json()
|
||||
}
|
||||
|
||||
export const getCharacter = async (characterId: string) => {
|
||||
const res = await fetch(`https://swapi.dev/api/people/${characterId}/`)
|
||||
return await res.json()
|
||||
}
|
||||
|
||||
export const getPlanet = async (planetId: string) => {
|
||||
const res = await fetch(`https://swapi.dev/api/planets/${planetId}/`)
|
||||
return await res.json()
|
||||
}
|
||||
6
examples/react/star-wars/src/index.tsx
Normal file
6
examples/react/star-wars/src/index.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
|
||||
const rootElement = document.getElementById('root')
|
||||
if (!rootElement) throw new Error('Missing #root element')
|
||||
ReactDOM.createRoot(rootElement).render(<App />)
|
||||
27
examples/react/star-wars/src/styles.css
Normal file
27
examples/react/star-wars/src/styles.css
Normal file
@@ -0,0 +1,27 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
@theme {
|
||||
--font-sans:
|
||||
Roboto, BlinkMacSystemFont, -apple-system, Segoe UI, Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, Fira Sans, Droid Sans, Helvetica Neue, Helvetica, Arial,
|
||||
sans-serif;
|
||||
--font-mono:
|
||||
Roboto Mono, BlinkMacSystemFont, -apple-system, Segoe UI, Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, Helvetica, Arial,
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
@apply font-sans;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
@apply font-mono;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user