I’m practicing my coding skills with React (latest version) using TypeScript. I am currently trying to build a Formula1 Application using Ergast API. To fetch data from the API, I am using RTKQuery. For styling I use TailwindCSS.
I have a StandingsSection component which has 3 buttons(Drivers,Constructors,Last Race) and for each button clicked it should display the corresponding list of data. The issue is that this data is not getting rendered in the UI, but it is correctly retrieved as far as I see by using some console.log().
So I’ve been stuck for a few hours already as I don’t see where the issue might be, if in the rendering logic or somewhere else like queries or interfaces. I’ll leave some code below.
Thank you in advance for the time you’ll spend reading all this terrible code
//apiSlice.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { NextEventResponse } from '../interfaces/NextEvent.interface';
import { ConstructorsStandingsResponse } from '../interfaces/ConstructorsStandings.interface';
import { DriversStandingsResponse } from '../interfaces/DriversStandings.interface';
import { LastRaceResultsResponse } from '../interfaces/LastResult.interface';
export const ergastApi = createApi({
reducerPath: 'ergastApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://ergast.com/api/f1' }),
endpoints: (builder) => ({
getNextEvent: builder.query<NextEventResponse, void>({
query: () => '/current.json',
}),
getConstructorsStandings: builder.query<ConstructorsStandingsResponse, void>({
query: () => '/current/constructorStandings.json',
}),
getDriversStandings: builder.query<DriversStandingsResponse, void>({
query: () => '/current/driverStandings.json',
}),
getLastRaceResults: builder.query<LastRaceResultsResponse, void>({
query: () => '/current/last/results.json',
})
}),
});
export const { useGetNextEventQuery, useGetConstructorsStandingsQuery, useGetDriversStandingsQuery, useGetLastRaceResultsQuery } = ergastApi;
//One of the interfaces I'm using.
interface DriverStanding {
position: string;
positionText: string;
points: string;
wins: string;
Driver: {
driverId: string;
permanentNumber: string;
code: string;
url: string;
givenName: string;
familyName: string;
dateOfBirth: string;
nationality: string;
};
Constructors: {
constructorId: string;
url: string;
name: string;
nationality: string;
}[];
}
export interface DriversStandingsResponse {
MRData: {
xmlns: string;
series: string;
url: string;
limit: string;
offset: string;
total: string;
StandingsTable: {
season: string;
StandingsLists: {
season: string;
round: string;
DriverStandings: DriverStanding[];
}[];
};
};
}
//StandingsSection component
import { useState } from 'react';
import {
useGetDriversStandingsQuery,
useGetConstructorsStandingsQuery,
useGetLastRaceResultsQuery,
} from '../slices/ergastApiSlice';
// Define the types for the different standings responses
interface DriverStanding {
Driver: {
givenName: string;
familyName: string;
};
points: string;
}
interface ConstructorStanding {
Constructor: {
name: string;
};
points: string;
}
interface LastRaceResult {
Driver?: {
givenName: string;
familyName: string;
};
Constructor?: {
name: string;
};
points: string;
}
const StandingsSection = () => {
// Define the state for the selected standings
const [selectedStandings, setSelectedStandings] = useState('Drivers');
const driversStandingsQuery = useGetDriversStandingsQuery();
const constructorStandingsQuery = useGetConstructorsStandingsQuery();
const lastRaceResultsQuery = useGetLastRaceResultsQuery();
// Define the function to handle the change of standings
const handleStandingsChange = (standingsType: string) => {
console.log('Selected Standings Type:', standingsType);
setSelectedStandings(standingsType);
};
// Get the data for the selected standings
let standingsData;
switch (selectedStandings) {
case 'Drivers':
standingsData = driversStandingsQuery;
break;
case 'Constructors':
standingsData = constructorStandingsQuery;
break;
case 'LastRace':
standingsData = lastRaceResultsQuery;
break;
default:
standingsData = driversStandingsQuery;
}
const isLoading = standingsData.isLoading;
const error = standingsData.error;
const standings = standingsData.data;
console.log('isLoading:', isLoading);
console.log('error:', error);
console.log('standings:', standings);
return (
<div className="p-8 bg-white rounded-lg shadow-md mb-4">
<div className="flex justify-between items-center mb-4">
<h2 className="text-2xl font-semibold">Standings</h2>
<div className="space-x-4">
<button
className={`${
selectedStandings === 'Drivers' ? 'bg-gray-800 text-white' : ''
} px-4 py-2 rounded-md`}
onClick={() => handleStandingsChange('Drivers')}
>
Drivers
</button>
<button
className={`${
selectedStandings === 'Constructors'
? 'bg-gray-800 text-white'
: ''
} px-4 py-2 rounded-md`}
onClick={() => handleStandingsChange('Constructors')}
>
Constructors
</button>
<button
className={`${
selectedStandings === 'LastRace' ? 'bg-gray-800 text-white' : ''
} px-4 py-2 rounded-md`}
onClick={() => handleStandingsChange('LastRace')}
>
Last Race
</button>
</div>
</div>
{isLoading && <div>Loading...</div>}
{error && <div>Error: {error?.toString() ?? 'Unknown error'}</div>}
{standings && Array.isArray(standings) && standings.length > 0 && (
<ul>
{standings.map((standing, index) => {
if (!standing) {
return null;
}
return (
<li key={index}>
{index + 1}. {getStandingName(standing)} - {standing.points}
</li>
);
})}
</ul>
)}
</div>
);
};
//Helper function to get the name of the driver or constructor
const getStandingName = (
standing: DriverStanding | ConstructorStanding | LastRaceResult
) => {
if ('Driver' in standing) {
return `${standing?.Driver?.givenName} ${standing?.Driver?.familyName}`;
} else if ('Constructor' in standing) {
return standing?.Constructor?.name;
} else {
return 'Unknown';
}
};
export default StandingsSection;
I checked all the endpoints responses and created the interfaces according to them. For “getNextEvent”, for example, it perfectly works as it shows the countdown to the next event in the calendar.
I suspect the issue might be in the component rendering logic but I’m not sure as I’m not very experienced.
Tiziano Sapienza is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.