react query

This commit is contained in:
Vinzenz Schroeter 2024-04-17 23:09:01 +02:00
parent 2a94a47a96
commit 25a3adea2a
8 changed files with 77 additions and 60 deletions

View file

@ -8,6 +8,7 @@
"name": "tank-frontend", "name": "tank-frontend",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@tanstack/react-query": "^5.29.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-use-websocket": "^4.8.1" "react-use-websocket": "^4.8.1"
@ -1188,6 +1189,30 @@
"win32" "win32"
] ]
}, },
"node_modules/@tanstack/query-core": {
"version": "5.29.0",
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.29.0.tgz",
"integrity": "sha512-WgPTRs58hm9CMzEr5jpISe8HXa3qKQ8CxewdYZeVnA54JrPY9B1CZiwsCoLpLkf0dGRZq+LcX5OiJb0bEsOFww==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
}
},
"node_modules/@tanstack/react-query": {
"version": "5.29.2",
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.29.2.tgz",
"integrity": "sha512-nyuWILR4u7H5moLGSiifLh8kIqQDLNOHGuSz0rcp+J75fNc8aQLyr5+I2JCHU3n+nJrTTW1ssgAD8HiKD7IFBQ==",
"dependencies": {
"@tanstack/query-core": "5.29.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
"react": "^18.0.0"
}
},
"node_modules/@types/babel__core": { "node_modules/@types/babel__core": {
"version": "7.20.5", "version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",

View file

@ -10,6 +10,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@tanstack/react-query": "^5.29.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-use-websocket": "^4.8.1" "react-use-websocket": "^4.8.1"

View file

@ -44,8 +44,8 @@ export default function App() {
<ClientScreen logout={logout} theme={theme} playerId={nameId.id}/> <ClientScreen logout={logout} theme={theme} playerId={nameId.id}/>
{nameId.name === '' && <JoinForm setNameId={setNameId} clientId={nameId.id}/>} {nameId.name === '' && <JoinForm setNameId={setNameId} clientId={nameId.id}/>}
<Row className='GadgetRows'> <Row className='GadgetRows'>
{isLoggedIn && <Controls playerId={nameId.id} logout={logout}/>} {isLoggedIn && <Controls playerId={nameId.id}/>}
{isLoggedIn && <PlayerInfo playerId={nameId.id} logout={logout}/>} {isLoggedIn && <PlayerInfo playerId={nameId.id}/>}
<Scoreboard/> <Scoreboard/>
</Row> </Row>
</Column>; </Column>;

View file

@ -1,19 +1,17 @@
import './Controls.css'; import './Controls.css';
import useWebSocket, {ReadyState} from 'react-use-websocket'; import useWebSocket, {ReadyState} from 'react-use-websocket';
import {useEffect} from 'react'; import {useEffect} from 'react';
import {Guid} from "./Guid.ts";
export default function Controls({playerId, logout}: { export default function Controls({playerId}: { playerId: Guid }) {
playerId: string,
logout: () => void
}) {
const url = new URL('controls', import.meta.env.VITE_TANK_WS); const url = new URL('controls', import.meta.env.VITE_TANK_WS);
url.searchParams.set('playerId', playerId); url.searchParams.set('playerId', playerId);
const { const {
sendMessage, sendMessage,
getWebSocket, getWebSocket,
readyState readyState
} = useWebSocket(url.toString(), { } = useWebSocket(url.toString(), {
onError: logout,
shouldReconnect: () => true, shouldReconnect: () => true,
}); });

View file

@ -1,43 +1,40 @@
import {useEffect, useState} from 'react'; import {useQuery} from '@tanstack/react-query'
import {Player, getPlayer} from './serverCalls'; import {Player} from './serverCalls';
import {Guid} from "./Guid.ts"; import {Guid} from "./Guid.ts";
import Column from "./components/Column.tsx"; import Column from "./components/Column.tsx";
export default function PlayerInfo({playerId, logout}: { export default function PlayerInfo({playerId}: { playerId: Guid }) {
playerId: Guid, const query = useQuery({
logout: () => void queryKey: ['player'],
}) { refetchInterval: 1000,
const [player, setPlayer] = useState<Player | null>(); queryFn: async () => {
const url = new URL('/player', import.meta.env.VITE_TANK_API);
url.searchParams.set('id', playerId);
useEffect(() => { const response = await fetch(url, {method: 'GET'});
const refresh = () => { if (!response.ok)
getPlayer(playerId).then(response => { throw new Error(`response failed with code ${response.status} (${response.status})${await response.text()}`)
if (response.successResult) return await response.json() as Player;
setPlayer(response.successResult); }
else });
logout();
});
};
const timer = setInterval(refresh, 5000);
return () => clearInterval(timer);
}, [playerId]);
return <Column className='PlayerInfo'> return <Column className='PlayerInfo'>
<h3> <h3>
{player ? `Playing as ${player?.name}` : 'loading...'} {query.isPending && 'loading...'}
{query.isSuccess && `Playing as ${query.data.name}`}
</h3> </h3>
<table> {query.isError && <p>{query.error.message}</p>}
{query.isSuccess && <table>
<tbody> <tbody>
<tr> <tr>
<td>kills:</td> <td>kills:</td>
<td>{player?.scores.kills}</td> <td>{query.data?.scores?.kills ?? '?'}</td>
</tr> </tr>
<tr> <tr>
<td>deaths:</td> <td>deaths:</td>
<td>{player?.scores.deaths}</td> <td>{query.data?.scores?.deaths ?? '?'}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>}
</Column>; </Column>;
} }

View file

@ -1,28 +1,31 @@
import {getScores, Player} from "./serverCalls.tsx"; import {Player} from "./serverCalls.tsx";
import {useEffect, useState} from "react";
import DataTable from "./components/DataTable.tsx"; import DataTable from "./components/DataTable.tsx";
import {useQuery} from "@tanstack/react-query";
function numberSorter(a: number, b: number) { function numberSorter(a: number, b: number) {
return b - a; return b - a;
} }
export default function Scoreboard({}: {}) { export default function Scoreboard({}: {}) {
const [players, setPlayers] = useState<Player[]>([]); const query = useQuery({
queryKey: ['scores'],
refetchInterval: 1000,
queryFn: async () => {
const url = new URL('/scores', import.meta.env.VITE_TANK_API);
const response = await fetch(url, {method: 'GET'});
if (!response.ok)
throw new Error(`response failed with code ${response.status} (${response.status})${await response.text()}`)
return await response.json() as Player[];
}
});
useEffect(() => { if (query.isError)
const refresh = () => { return <p>{query.error.message}</p>;
getScores().then(value => { if (query.isPending)
if (value.successResult) return <p>loading...</p>;
setPlayers(value.successResult);
});
};
const timer = setInterval(refresh, 5000);
return () => clearInterval(timer);
}, [players]);
return <DataTable return <DataTable
data={players} data={query.data}
columns={[ columns={[
{field: 'name'}, {field: 'name'},
{ {

View file

@ -2,9 +2,14 @@ import React from 'react';
import {createRoot} from 'react-dom/client'; import {createRoot} from 'react-dom/client';
import './index.css'; import './index.css';
import App from "./App.tsx"; import App from "./App.tsx";
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
const queryClient = new QueryClient()
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById('root')!).render(
<React.StrictMode> <React.StrictMode>
<App/> <QueryClientProvider client={queryClient}>
<App/>
</QueryClientProvider>
</React.StrictMode> </React.StrictMode>
); );

View file

@ -44,15 +44,3 @@ export function postPlayer({name, id}: NameId) {
return fetchTyped<NameId>({url, method: 'POST'}); return fetchTyped<NameId>({url, method: 'POST'});
} }
export function getPlayer(id: Guid) {
const url = new URL('/player', import.meta.env.VITE_TANK_API);
url.searchParams.set('id', id);
return fetchTyped<Player>({url, method: 'GET'});
}
export function getScores() {
const url = new URL('/scores', import.meta.env.VITE_TANK_API);
return fetchTyped<Player[]>({url, method: 'GET'});
}