I'm pretty now to GraphQL and TypeScript, so i'm trying to learn it by working with this API https://github.com/trevorblades/countries . I'm trying to fetch data from the API to my React application in my HomeComponent and then pass the data to my CountryTable component, which is a Table component from the MUI library. But i get these error as "Argument of type is not assignable to parameter", "TypeError: countries.map is not a function".
This is how my HomeComponent looks like:
import { Grid } from '@mui/material';
import Typography from '@mui/material/Typography';
import { ApolloClient, InMemoryCache, gql, useLazyQuery, ApolloProvider, useQuery, TypedDocumentNode} from '@apollo/client';
import { useEffect, useState } from 'react';
import React from 'react';
import CountryTable from '../components/CountryTable';
const client = new ApolloClient({
cache: new InMemoryCache(),
uri: 'https://countries.trevorblades.com'
});
const LIST_COUNTRIES = gql`
query getCountries {
countries {
name
code
capital
}}`;
export interface Country {
name: string;
code: string;
capital: string;
}
export interface Countries {
getAllCountries: Country[];
}
export default function Home() {
const {loading, data, error} = useQuery<Countries>(LIST_COUNTRIES, {client});
const [allcountries, setAllcountries] = useState([]);
useEffect(() => {
setAllcountries(data);
}, [data])
if (loading) return <p>Loading...</p>;
if (error) return <p>Error : {error.message}</p>;
return (
<ApolloProvider client={client}>
<Grid container alignItems="center" justifyContent="center">
<Grid item>
<CountryTable countries={allcountries}/>
</Grid>
</Grid>
</ApolloProvider>
)
}
And here is my CountryTable component:
import { Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material";
import { Country } from "../pages";
export interface CountryProps {
countries: Country[];
}
export default function CountryTable({countries}: CountryProps) {
return(
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell align="right">Name</TableCell>
<TableCell align="right">Code</TableCell>
<TableCell align="right">Capital</TableCell>
</TableRow>
</TableHead>
<TableBody>
{countries?.map((row: any) => (
<TableRow key={row.name}>
<TableCell align="right">{row.name}</TableCell>
<TableCell align="right">{row.code}</TableCell>
<TableCell align="right">{row.capital}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
The projectstructure: ProjectStructure
You are calling the useQuery
hook at the same level where the ApolloProvider
is being wrapped.
// Home.js
const {loading, data, error} = useQuery<Countries>(LIST_COUNTRIES, {client}); // 👈 same level
...
...
return (
<ApolloProvider client={client}> {/* 👈 Same level */}
..
..
</ApolloProvider>
)
The reason why moving the useQuery
hook you have from Home
to CountryTable
component worked is because now
CountryTable
is inside ApolloProvider
. useQuery
hook can access the client
data it needed, which wasn't possible earlier when they were defined at the same level. To make it working in the same level, move the ApolloProvider
a level up.
This can be either
Home
component if you have any or, _app.js
component if you're using NextJS
[1] Reference:
The problem why i could not fetch the data from the GraphQL API was because that my data that i was retrieving was as an whole object and the useState variabel which i'm using to save my data in so I can pass it as props to another component was an ArrayType of the interface Country. So i have removed the interface Countries and then defined my const variabel as following:
const {loading, data, error} = useQuery<{countries: Country[]}>(LIST_COUNTRIES, {client});
const [allcountries, setAllcountries] = useState<Country[]>();
And then my component which receives the props have the following code:
import { Box, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material";
import Link from "next/link";
import { useRouter } from "next/router";
import { FC } from "react";
import { Country } from "../pages";
export interface CountryProps {
countries: Country[] | undefined;
}
const CountryTable: FC<CountryProps> = ({countries}) => {
const router = useRouter();
return(
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell align="right">Name</TableCell>
<TableCell align="right">Code</TableCell>
<TableCell align="right">Capital</TableCell>
</TableRow>
</TableHead>
<TableBody>
{countries?.map(({name, code, capital}: Country) => (
<TableRow key={name}>
<TableCell align="right">
<Box>
<Link href={{pathname: "/detail/[name]", query: {name: name}}}>
{name}
</Link>
</Box>
</TableCell>
<TableCell align="right">{code}</TableCell>
<TableCell align="right">{capital}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
export default CountryTable;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.