Home > Software design >  Fetching data from YouTube API with React Hooks
Fetching data from YouTube API with React Hooks

Time:09-23

I am trying to populate a page with the playlists on my YouTube channel. I am using React 16 with hooks.

import React, { useEffect, useState } from 'react'
import { Container, Row, Col, Card } from 'react-bootstrap'
import PageHeader from '../components/PageHeader'
import axios from "axios"

const Tutorials = () => {
  const [data, setData] = useState({items:[]})
  
  useEffect(() => {
    const fetchData = async () => {
      const results = await axios(
        'https://youtube.googleapis.com/youtube/v3/playlists?part=snippet&channelId=[CHANNEL_ID]&key=[API_EXISTS_AND_TESTED_IN_POSTMAN]'
      );
      setData(results.data.items) 
      console.log(results.data.items);
    }
    fetchData();
  }, [data])

  return (
    <div className="">
      <Container>
        <PageHeader title="Tutorials" />
        <Row xs={1} md={2} className="g-4">
          {data && data.map(item => {
            return(
              <Col xs={12} lg={4}>
                <Card  className="bg-dark">
                  <Card.Img variant="top" src={item.snippet.thumbnails.high.url} />
                  <Card.Body>
                    <Card.Title>{item.snippet.title}</Card.Title>
                    <Card.Text>
                      {item.snippet.description}
                    </Card.Text>
                  </Card.Body>
                </Card>
              </Col>
            )
          })}
        </Row>
      </Container>
    </div>
  )
}

export default Tutorials

Here is what I am getting. However, if I delete the .map() entire function and render and then paste it and render, it will show. So the connection is happening. But as soon as I refresh, I get this. What gives? I thought maybe the data needs time to load, so I put the setData() in a setTimeout and also checked to see if the data is available before I try to render it and nothing worked. Postman shows my data coming back. YouTube, by the way doesn't provide an array back. It returns an Object. Screenshot 1

I am also getting a 403 which I am not sure if it's related enter image description here

CodePudding user response:

The problem is that you are initialising the state data as an object

  const [data, setData] = useState({items:[]})

and when the component makes the first render it tries to map an object which is not possible. So you should initialise the data state as an array:

  const [data, setData] = useState([])

Also the {data && ...} is not necessary since data is an array which is a truthy value.

The second problem in the code is with the useEffect because you have data as a dependency and you are setting the data inside the useEffect which could cause the useEffect to be called again. The correct way is an empty array as second argument:

  useEffect(() => {
const fetchData = async () => {
  const results = await axios(
    'https://youtube.googleapis.com/youtube/v3/playlists?part=snippet&channelId=[CHANNEL_ID]&key=[API_EXISTS_AND_TESTED_IN_POSTMAN]'
  );
  setData(results.data.items) 
  console.log(results.data.items);
}
fetchData();
}, [])

CodePudding user response:

try to initialize it with an empty array instead of an object

const [data, setData] = useState([])

CodePudding user response:

Why you get the error

First you setup state with a default value:

const [data, setData] = useState({ items:[] })

This sets data to { items: [] }, an object with a property named items, that has an array as its value.

Then your component renders and runs this line:

data && data.map(item => {

But { items: [] } is not an array, and so it does not have a map method. This is where that error comes from.

Then after that render you update the state from an effect:

setData(results.data.items)

This (probably) sets data to an array, which is a different format than your initial default value for the state.


How to fix the error

You either want to use an array as the default:

const [data, setData] = useState([])

Then your other code should work fine.

Or

you want to use the items property when you read/write your state.

setData({ items: results.data.items })

And something like:

data.items.map(item => {

useEffect hook dependencies

Lastly, this will cause an infinite re-render cycle:

  useEffect(() => {
    const fetchData = async () => {
      const results = await axios(
        'https://youtube.googleapis.com/youtube/v3/playlists?part=snippet&channelId=[CHANNEL_ID]&key=[API_EXISTS_AND_TESTED_IN_POSTMAN]'
      );
      setData(results.data.items) 
      console.log(results.data.items);
    }
    fetchData();
  }, [data])

data is declared as a dependency of this effect, but this hook writes to data. So this effect will cause itself to re-render, ad infinitum.

However, this effect does not actually depend on data, so it can be omitted. In this case an empty array [] should be used as the effect dependencies the effect does not actually use any local variables or state.

  • Related