简体   繁体   中英

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

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 .
  • The 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

  • The parent component of Home component if you have any or,
  • The _app.js component if you're using NextJS [1]

Reference:

  1. https://nextjs.org/docs/advanced-features/custom-app

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM