skip to Main Content

I have started my news project developing using React js. Unfortunately I have an issue. I am using axios for data fetching. I am making a request and I have an error in the console. I tried to use useState instead of variable posts in main file, but I had received the same error. I think, that something wrong either with posts variable, because I think, that useEffect is working slower, than html code, that will be returned or with map method.

Error:

TypeError: Cannot read properties of undefined (reading map) at news.jsx

Post file:

import React from 'react';

function Post(props) {
    return (
        <div className="post">
            <div className="post-name">{props.title}</div>
            <div className="post-content">{props.text}</div>
            <a className="post-source" href={props.url}>{props.name}</a>
        </div>
    );
}

export default Post;

Main file with requests:

import React, { useEffect } from "react";
import SyncIcon from "@mui/icons-material/Sync";
import axios from "axios";
import "../css/news.css";
import Post from "./Post";

function News() {
  let posts;

  useEffect(() => {
    const loading = document.querySelector(".loading");
    const postsContainer = document.querySelector(".news-posts");

    async function loadPosts() {
      const date = new Date();

      const day = date.getDate();

      const month = date.getMonth();

      const year = date.getFullYear();

      const fullDate = year + "-0" + month + "-0" + day;

      let response = [];

      try {
        const request = await axios.get(
          `https://newsapi.org/v2/everything?qInTitle=Ukraine&from=${fullDate}&sortBy=publishedAt&apiKey=363858d3a88f49ffad9b467282270c8a`
        );

        const data = request.data.articles;

        for (let i = 0; i < 20; i++) {
          response.push({
            source: {
              name: data[i].source.name,
              url: data[i].url,
            },

            content: {
              title: data[i].title,
              text: data[i].content,
            },
          });
        }
      } catch {
        console.log("error");
      }

      loading.classList.add("none");
      // setPosts(response);
      posts = response;
    }

    loadPosts();
  }, []);

  return (
    <section className="news-container">
      <div className="news-posts">
        <div className="loading">
          <SyncIcon />
        </div>
        {posts.map((post) => (
          <Post
            name={post.source.name}
            url={post.source.url}
            text={post.content.text}
            title={post.content.title}
          />
        ))}
      </div>
    </section>
  );
}

export default News;

2

Answers


  1. This is one of the proper ways to fetch data and display them:

    import React, { useEffect } from "react";
    import SyncIcon from "@mui/icons-material/Sync";
    import axios from "axios";
    import "../css/news.css";
    import Post from "./Post";
    
    function News() {
      const [posts, setPosts] = useState([]) // init state as empty array. We will store posts here
      const [loading, setLoading] = useState(true) //Start with loading = true
     const [error, setError] = useState(false) //Start with error = false 
    
      useEffect(() => {
        async function loadPosts() {
          const date = new Date();
    
          const day = date.getDate();
    
          const month = date.getMonth();
    
          const year = date.getFullYear();
    
          const fullDate = year + "-0" + month + "-0" + day;
    
         
    
          try {
            const request = await axios.get(
              `https://newsapi.org/v2/everything?qInTitle=Ukraine&from=${fullDate}&sortBy=publishedAt&apiKey=363858d3a88f49ffad9b467282270c8a`
            );
    
            const data = request.data.articles;
            
            for (let i = 0; i < 20; i++) {
              response.push({
                source: {
                  name: data[i].source.name,
                  url: data[i].url,
                },
    
                content: {
                  title: data[i].title,
                  text: data[i].content,
                },
              });
            }
    
          setPosts(response); //set state
          setLoading(false) //end loading
        
          } catch {
            console.log("error");
            setError(true)
            setLoading(false)
          }
    
          
    
        }
    
        loadPosts();
      }, []);
    
      return (
        <section className="news-container">
          <div className="news-posts">
            {loading? 
            (<div className="loading">
              <SyncIcon />
            </div>)
       : null }
            {posts?.length? posts.map((post) => (
              <Post
                name={post.source.name}
                url={post.source.url}
                text={post.content.text}
                title={post.content.title}
              />
            ))} : <p>No data found</p>
          </div>
        </section>
      );
    }
    
    export default News;
    
    

    Basically, the error is that your posts are undefined. And trying to map it breaks your app. You need to check if it exists and if it is an array, then map trough it. Also, the React way to render loading or error components is to use conditional rendering in the return function (check loading)

    Also, you must use state otherwise React wont know if it needs to rerender anything.

    You do not need to use query selectors

    Login or Signup to reply.
  2. You should use useState for updating content in your component.

    The if statements I’ve implemented are in order to ensure the content received is not empty. You can remove those after you’ve debugged this or implement different actions under those conditions.

    import React, { useEffect, useState } from "react";
    import SyncIcon from "@mui/icons-material/Sync";
    import axios from "axios";
    import "../css/news.css";
    import Post from "./Post";
    
    function News() {
      const [posts, setPosts] = useState([]);
    
      useEffect(async () => {
        const loading = document.querySelector(".loading");
        const postsContainer = document.querySelector(".news-posts");
    
        const date = new Date();
        const day = date.getDate();
        const month = date.getMonth();
        const year = date.getFullYear();
        const fullDate = year + "-0" + month + "-0" + day;
        let response = [];
    
        try {
          const request = await axios.get(
            `https://newsapi.org/v2/everything?qInTitle=Ukraine&from=${fullDate}&sortBy=publishedAt&apiKey=363858d3a88f49ffad9b467282270c8a`
          );
    
          if (request.data) {
            const data = request.data.articles;
    
            if (data) {
              for (let i = 0; i < 20; i++) {
                response.push({
                  source: {
                    name: data[i].source.name,
                    url: data[i].url,
                  },
    
                  content: {
                    title: data[i].title,
                    text: data[i].content,
                  },
                });
              }
    
              setPosts(response);
            } else {
              console.log("No articles in response data");
            }
          } else {
            console.log("Empty response");
          }
        } catch (err) {
          console.log(`Error: ${err}`);
        }
    
        loading.classList.add("none");
      });
    
      return (
        <section className="news-container">
          <div className="news-posts">
            <div className="loading">
              <SyncIcon />
            </div>{" "}
            {posts.map((post) => (
              <Post
                name={post.source.name}
                url={post.source.url}
                text={post.content.text}
                title={post.content.title}
              />
            ))}
          </div>{" "}
        </section>
      );
    }
    
    export default News;
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search