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.
I am also getting a 403 which I am not sure if it's related
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.