Search code examples
javascriptreactjsapiuse-effect

Why articles[0] is undefined?


I'm trying to dynamically set the src attribute of my <img /> component with data fetched by axios but but constantly fail. I don't know how to get the value of urlToImage property out of the fetched data object.

I have tried const mainArticle = articles[0].urlToImage; and then setting the src={mainArticle}. I can console.log(articles[0]) but not console.log(articles[0].title) becuase it suddenly turns to undefined. Any idea why this happens?

import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';

import AsideNews from '../../components/aside-news/aside-news.component';

import './front-page.styles.scss';

const FrontPage = () => {
  const [articles, setArticles] = useState([]);

  useEffect(() => {
    const url =
      'https://newsapi.org/v2/everything?' +
      'q=guitar&' +
      'from=2019-10-17&' +
      'sortBy=popularity&' +
      'apiKey=thisIsSecret';

    const getData = async () => {
      const response = await axios
        .get(url)
        .then(console.log('isFetched'))
        .catch(err => console.error(err));
      const { data } = response;
      setArticles(data.articles);
    };
    getData();
  }, []);

  return (
    <div className='frontpage' articles={articles}>
      <main className='main-content'>
        <section className='first-section'>
          <AsideNews articles={articles} />

          <div className='main-headline'>
            <img
              src={articles[0].urlToImage}
              alt='main-headline'
              height='400px'
              width='90%'
            />
            <div className='main-headline-desc'>
              {articles
                .filter((article, idx) => idx === 0)
                .map(article => (
                  <p>{article.description}</p>
                ))}
            </div>

            <div className='main-headline-title'>
              {articles
                .filter((article, idx) => idx === 0)
                .map(article => (
                  <Link to='/'>
                    <h2>{article.title}</h2>
                  </Link>
                ))}
            </div>
          </div>
        </section>
      </main>
    </div>
  );
};

export default FrontPage;

I expected to work the same as article.title, but this is what I get in stead: TypeError: articles[0] is undefined


Solution

  • Hi you could always check if articles have been loaded before using it like this:

    <img src={articles && articles[0].urlToImage} alt='main-headline' height='400px' width='90%'/>
    

    The '&&' operator checks if it exists before trying to load it. Downside is you might have some elements of your page load while the image still remains blank.

    Another option is to have an isFetched state which toggles to true when the data is loaded and you render the entire component conditionally if the data is loaded.

    const[isFetched, setIsFetched] = useState(false)
    
        const getData = async () => {
          const response = await axios
            .get(url)
            .then(()=> {
               setIsFetched(true)
    
               //write remainder of your function
    
        };
        getData();
      }, []);
    
    return (!isFetched ? 'data loading' :
       <div>Your div component</div>)