Я начал разработку своего новостного проекта с использованием React js. К сожалению, у меня есть проблема. Я использую axios для выборки данных. Я делаю запрос, и у меня есть ошибка в консоли. Я попытался использовать useState вместо переменных сообщений в основном файле, но получил ту же ошибку. Я думаю, что что-то не так либо с переменной posts, потому что я думаю, что useEffect работает медленнее, чем html-код, который будет возвращен или с методом карты.
Ошибка:
TypeError: Cannot read properties of undefined (reading map) at news.jsx
Почтовый файл:
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;
Основной файл с запросами:
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.info("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;
Я попробовал сначала useState, но у меня была та же ошибка. Поэтому из-за этого я использовал переменную
Скорее всего, потому что ваше начальное состояние не было массивом, и вы не имели с этим дело.
Ладно, понял. Можете ли вы добавить ответ на мой вопрос с помощью useState в коде. Потому что я перепробовал все, что мог на самом деле.
Это один из правильных способов получения данных и их отображения:
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.info("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;
По сути, ошибка в том, что ваши сообщения не определены. И попытка отобразить это сломает ваше приложение. Вам нужно проверить, существует ли он, и если это массив, а затем сопоставить его. Кроме того, способ React для рендеринга компонентов загрузки или ошибок заключается в использовании условного рендеринга в функции возврата (проверьте загрузку).
Кроме того, вы должны использовать состояние, иначе React не узнает, нужно ли ему что-либо перерисовывать.
Вам не нужно использовать селекторы запросов
Вы должны использовать useState для обновления контента в вашем компоненте.
Операторы if, которые я реализовал, предназначены для обеспечения того, чтобы полученный контент не был пустым. Вы можете удалить их после того, как отладите это, или выполните другие действия в этих условиях.
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.info("No articles in response data");
}
} else {
console.info("Empty response");
}
} catch (err) {
console.info(`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;
Fetch асинхронный, вам нужно где-то useState.