Home > Software design >  TypeError: Cannot read properties of undefined (reading 'map'), and multiple API requests
TypeError: Cannot read properties of undefined (reading 'map'), and multiple API requests

Time:11-15

I know this map reading error has been asked a lot here before and I looked at several closed issues, but since their causes were different from mine I've opted to open a new topic. At the moment I'm facing 2 errors, so let me tackle each at a time.

What I want to accomplish: After submitting a search query, the "Results" component should be rendered and make a GET request to my back-end (which, in turn, will make a GET request to the external API and send back the results to React). Once it fetches the data it should store it in the details variable, which will be mapped and rendered in a child component.

First problem:

Despite a successful request is being made to my back-end and also to the external API - which I could check by using console.log -, it seems like the retrieved data isn't being stored inside the variable, resulting in an undefined array to be mapped. So, what am I doing wrong here? Is this happening because the component is trying to map the array before the GET request completes and executes the setDetails(response.data) even though it comes way below in the code?

function Results() {
  const location = useLocation();
  const history = useHistory();
  const [query, setQuery] = useState(location.state.search);
  const [details, setDetails] = useState([]);

  axios.get("http://localhost:5000/results/"   query).then((response) => {
    setDetails(response.data);
    console.log(response.data);
  });

  const results = details.total_results;
  const pages = details.total_pages;
  const baseURL = "https://image.tmdb.org/t/p/w154";

  return (
    <Container>
      <Card sx={{ padding: 3, margin: 2 }}>
        <CardContent>
          <Grid container>
            <Grid item xs={12}>
              <Typography variant="h5">Results</Typography>
              <Typography>{results ? results : 0} results found</Typography>
            </Grid>
            <Grid item xs={12}>
              <List sx={{ mt: 5 }}>
                {details.results.map((result) => {
                  return (
                    <ResultItem
                      key={result.id}
                      imgURL={baseURL   result.poster_path}
                      title={result.title}
                      year={result.release_date}
                      director=""
                      synopsis={result.overview}
                    />
                  );
                })}
              </List>
            </Grid>
          </Grid>
        </CardContent>
        <Pagination
          count={pages}
          onChange={(event, page) => {
            history.push("/results"   query   "&page="   page);
            setQuery(query   "&page="   page);
          }}
        />
      </Card>
    </Container>
  );
}

export default Results;

Second problem:

I'm not sure exactly why, but the GET request is triggering two responses from the external API instead of just one. Both, front and back-end, are logging the retrieved data two times, and I suspect the multiple requests are coming from my front-end, causing the back-end to trigger multiple requests as well. And that is a huge step from a previous error when something in my code was triggering an infinite loop of requests.

I'll probably have issues with the pagination later on as well, I guess, but that's another story.

CodePudding user response:

Issue One

Initial details state is an array, not an object with a results array property.

const [details, setDetails] = useState([]);

so attempting to map details.results results in the error "cannot read property X of undefined.

To resolve, use valid initial details state that matches how it's used later. It should be an object with a valid results array property.

const [details, setDetails] = useState({ results: [] });

Issue Two

You are invoking the axios request right in the body of the function component, as an unintentional side-effect. This is likely the result of rendering your app into a React.StrictMode component which double-invokes certain lifecycle methods and functions as a way to detect unexpected side-effects, the function body being one of them.

You should move this into an useEffect hook with the query as a dependency.

useEffect(() => {
  axios.get("http://localhost:5000/results/"   query)
    .then((response) => {
      setDetails(response.data);
    });
}, [query]);
  • Related