menab
menab

Reputation: 57

Trying to fetch data from a GraphQL API and then pass it as props to a component

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

Upvotes: 1

Views: 202

Answers (1)

menab
menab

Reputation: 57

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;

Upvotes: 1

Related Questions