I don't understand why my fetched data is not displaying.
Data is fetched properly when i check by console.log() however nothing shows up in my JSX section. Code looks alight too. Anyone have idea what is wrong here?
import React from "react";
import { useEffect, useState } from "react";
import axios from "axios";
export const ConcertLatest = () => {
const [concerts, setConcerts] = useState([]);
useEffect(() => {
const loadConcerts = async () => {
const response = await axios.get("/data/concerts");
const rawData = response.data;
const filteredData = rawData.filter((concert) => {
//let date = new Date(concert.datum);
// let newestDate = new Date("2022-09-29");
return concert.datum >= "2022-09-30";
});
setConcerts(filteredData);
};
loadConcerts();
}, []);
if (!concerts.length) {
return <p>Loading...</p>;
}
console.log(concerts); // getting full populated objects
return (
<div>
<h1>Newest concerts </h1>
<div>
<div className="card">
<img src={concerts.image} style={{ width: 100 }} />
<div className="card-text">
<div>
{concerts.map((concert) => {
(<h1>{concert.name}</h1>), (<h2>{concert.datum}</h2>);
})}
</div>
</div>
</div>
</div>
</div>
);
};
Change syntax of map function, either use return keyword with curly braces or just use round braces without return keyword. Eg
<div>
{concerts.map((concert) => {
return (
<React.Fragment>
(<h1>{concert.name}</h1>), (<h2>{concert.datum}</h2>)
</React.Fragment>
)
})}
</div>
Or
<div>
{concerts.map((concert) => (
<React.Fragment>
(<h1>{concert.name}</h1>), (<h2>{concert.datum}</h2>)
</React.Fragment>
))}
</div>
Related
I want to fetch the data when the button is clicked but the Newsitem component is running first and then updating the value of data_grabber. That means it is displaying the defalut values rather than the data that I fetched from the newsapi. After displaying the newsitem component with default values, data_grabber is updating the fetched data.
What can be the solution?
App.js
function App() {
const [input_data, setInput_data] = useState("");
const [btn_data, setBtn_data] = useState("");
const [data_grabber, setData_grabber] = useState([]);
return (
<>
<Navbar
input_data={input_data}
setInput_data={setInput_data}
setBtn_data={setBtn_data}
btn_data={btn_data}
data_grabber={data_grabber}
setData_grabber={setData_grabber}
/>
{data_grabber? data_grabber.map((news_data)=>{
return(
<NewsItem news_data={news_data}/>
)
}):<div>No data available</div>}
</>
);
}
export default App;
Navbar.js
import { useEffect } from "react";
export default function Navbar(props) {
const onClicker = async (e) => {
e.preventDefault();
props.setBtn_data(props.input_data);
};
useEffect(() => {
const fetcher = async () => {
const link = `https://newsapi.org/v2/everything?q=${props.btn_data}&apiKey=API_KEY`;
const raw_data = await fetch(link);
const data = await raw_data.json();
console.log(data);
props.setData_grabber(data.articles)
};
fetcher();
}, [props.btn_data]);
return (
<div>
<form className="d-flex">
<input
onChange={(e) => props.setInput_data(e.target.value)}
value={props.input_data}
className="form-control me-2"
type="search"
placeholder="Search"
aria-label="Search"
/>
<button
className="btn btn-outline-success"
type="submit"
onClick={onClicker}
>
Search
</button>
</form>
</div>
NewsItem.js
import React, { Component } from "react";
export default class NewsItem extends Component {
render() {
const {title, description, url, urlToImage} = this.props.data
const defaultImage = `https://blogger.googleusercontent.com/img/a/AVvXsEh20SgNNsDlKyWWmB7XgB5SfFY10M6CqJAq93HwGtssTn2cWz6w9zHPjXf91WwoWr27QeaC4HsGv2NxPOXUdvk6xodUojnw8rUuAkEMY3Qb4ucoVpN3nSyF8JW_xVDWa2aSMEWH387hPsfouSJyClLNburIcDbXIeJamuTHwiSvw4hdNnqeeICcvg1wrQ=w1200-h630-p-k-no-nu`
return (
<div>
<div className="card">
<img src={urlToImage?urlToImage:defaultImage} className="card-img-top" alt="..." />
<div className="card-body">
<h5 className="card-title">{title?title:'No title available'}</h5>
<p className="card-text">
{description?description.slice(0, 50):"no description available"}...
</p>
<a href={url} target="_blank" rel="noreferrer"className="btn btn-primary">
read more
</a>
</div>
</div>
</div>
);
}
}
One fix could be to
make a variable of the updated state:
in the UseEffect ,
add :
const updated = data.articles
props.setData_grabber(updated)
Check whether data_grabber array is empty or not and then do the rendering inside App component as follows.
{
data_grabber.length > 0 ? (
data_grabber.map((news_data) => {
return <NewsItem news_data={news_data} />;
})
) : (
<div>No data available</div>
);
}
{ data_grabber !== undefined && data_grabber.length > 0 ? data_grabber.map((news_data)=>{
return(
<NewsItem news_data={news_data}/>
)
}):<div>No data available</div>}
Check data_grabber is undefined or empty.
Then, fix NewsItem props.data like this.
export default class NewsItem extends Component {
render() {
const {title, description, url, urlToImage} = this.props.news_data
also fix here in useEffect
useEffect(() => {
const fetcher = async () => {
const link = `https://newsapi.org/v2/everything?q=${props.btn_data}&apiKey=c990aa0235da4635997afd1f7459860c`;
const raw_data = await fetch(link);
const data = await raw_data.json();
console.log(data);
if(data.articles){
props.setData_grabber(data.articles)
}
};
fetcher();
I'm trying to present the movie API array data in bootstrap cards but it seems that I still don't understand something about react hooks or map functions because I can't get the code right. I get this "TypeError: setTrendingResults.map is not a function" .
My code:
import Hero from "./Hero";
import Footer from "./Footer";
import { Link } from "react-router-dom";
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
const TrendingCard = ({ trending }) => {
const trendPosterUrl = `https://image.tmdb.org/t/p/w500${trending.poster_path}`;
const trendDetailUrl = `/movies/${trending.id}`;
return (
<div className="col-lg-4 col-md-3 col-2">
<div className="card">
<img
src={trendPosterUrl}
class="card-img-top"
alt={trending.original_title}
/>
<div className="card-body">
<h5 className="card-title">{trending.original_title}</h5>
<Link to={trendDetailUrl} class="btn btn-primary">
Show details
</Link>
</div>
</div>
</div>
);
};
const TrendingView = (data) => {
const [trendingResults, setTrendingResults] = useState([]);
useEffect(() => {
fetch(
"https://api.themoviedb.org/3/trending/movie/week?api_key=776d38251dae661e04c01631cfa95286"
)
.then((response) => response.json())
.then((data) => {
setTrendingResults(data.results);
});
});
const trendingHtml = setTrendingResults.map((obj, i) => {
return <TrendingCard trending={obj} key={i} />;
});
return (
<div>
<Hero text="Trending" />
{trendingHtml && (
<div className="container">
<div className="row">{trendingHtml}</div>
</div>
)}
</div>
);
};
export default TrendingView;
Use valid state for mapping a card. trendingResults insted of setTrendingResults.
Here I convert a trendingHtml with React.useMemo which helps to avoid re-render-related stuff.
const TrendingView = (data) => {
const [trendingResults, setTrendingResults] = useState([]);
useEffect(() => {
fetch(
"https://api.themoviedb.org/3/trending/movie/week?api_key=776d38251dae661e04c01631cfa95286"
)
.then((response) => response.json())
.then((data) => {
setTrendingResults(data.results);
});
});
const trendingHtml = React.useMemo(()=> trendingResults?.map((obj, i) => {
return (<TrendingCard trending={obj} key={i} />)
}),[trendingResults])
return (
<div>
<Hero text="Trending" />
{trendingHtml && (
<div className="container">
<div className="row">{trendingHtml}</div>
</div>
)}
</div>
);
};
export default TrendingView;
setTrendingResults is used to set the value to trendingResults state. You have to use trendingResults instead of setTrendingResults.
const trendingHtml = trendingResults.map((obj, i) => {
return <TrendingCard trending={obj} key={i} />
})
I want to update render when a special property changes. This property income from parents. I Made a useState called loader to handle codes when I have data or not. if the loader is false, my code calls API and if it is true render data.
First of all I use useEffect this way. It didn't update render
useEffect(() => {
callApi();
}, []);
After that I used useEffect this way. props.coordinates is a property that my code should update after it changes.
useEffect(() => {
callApi();
setLoader(false);
}, [props.coordinates]);
But my codes are in loops, and my API key was blocked.
Could you let me know what my mistake is ?
This my component:
import React, { useEffect, useState } from "react";
import axios from "axios";
import ForcastHour from "./ForcastHour";
import "./WeatherHourlyForcast.css";
const WeatherHourlyForcast = (props) => {
const [loader, setLoader] = useState(false);
const [hourlyForcastData, setHourlylyForcastData] = useState(null);
useEffect(() => {
callApi();
setLoader(false);
}, [props.coordinates]);
const showHourlyForcast = (response) => {
console.log("showHourlyForcast", response.data.hourly);
setHourlylyForcastData(response.data.hourly);
setLoader(true);
};
function callApi() {
let latitude = props.coordinates.lat;
let longitude = props.coordinates.lon;
const apiKey = "23422500afd990f6bd64b60f46cf509a";
let units = "metric";
let apiUrl = `https://api.openweathermap.org/data/2.5/onecall?lat=${latitude}&lon=${longitude}&appid=${apiKey}&units=${units}`;
axios.get(apiUrl).then(showHourlyForcast);
console.log("hourly", apiUrl);
}
if (loader) {
return (
<div className="row">
<div className="col-md-6">
<div className="row">
{hourlyForcastData.map(function (hourlyforcast, index) {
if (index < 4 && index > 0) {
return (
<div
className="col-4 box-weather my-auto text-center"
key={index}
>
<ForcastHour data={hourlyforcast} />
</div>
);
}
})}
</div>
</div>
<div className="col-md-6">
<div className="row">
{hourlyForcastData.map(function (hourlyforcast, index) {
if (index < 7 && index > 3) {
return (
<div
className="col-4 box-weather my-auto text-center"
key={index}
>
<ForcastHour data={hourlyforcast} />
</div>
);
}
})}
</div>
</div>
</div>
);
} else {
callApi();
return null;
}
};
export default WeatherHourlyForcast;
While adding dependencies array to the end of useEffect (or any other hook...), each render if the value is not equal to the prev one, the hook will run again.
Because props.coordinates is an object, and in JS objA != objA == true, even if the properties didn't change, React can't know that.
My suggestion is to use the values themselves (assuming they're strings either numbers and so on)
useEffect(() => {
(async () => {
await callApi();
setLoader(false);
})()
}, [props.coordinates.lat, props.coordinates.lon]);
Another thing that you might encounter is setLoader(false) will be called before callApi will be finished, therefore added async behaviour to the hook
You can write your component likes this and call the APIs when the component mount. The API calls happens when the lat, lon values are changed.
import React, { useEffect, useState } from "react";
import axios from "axios";
import ForcastHour from "./ForcastHour";
import "./WeatherHourlyForcast.css";
const WeatherHourlyForcast = (props) => {
const { coordinates : { lat, lon } } = props;
const [loader, setLoader] = useState(false);
const [hourlyForcastData, setHourlylyForcastData] = useState(null);
useEffect(() => {
callApi();
}, [lat, lon]); //It's call the API's when the lat, lon values are changed
const callApi = () => {
setLoader(true);
const latitude = lat;
const longitude = lon;
const apiKey = "23422500afd990f6bd64b60f46cf509a";
const units = "metric";
const apiUrl = `https://api.openweathermap.org/data/2.5/onecall?lat=${latitude}&lon=${longitude}&appid=${apiKey}&units=${units}`;
axios.get(apiUrl).then((response) => {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
console.log("showHourlyForcast", response.data.hourly);
setHourlylyForcastData(response.data.hourly);
setLoader(false);
});
};
if (loader) {
return (
<div>
<h1>Loading...</h1>
</div>
);
}
return (
<div className="row">
<div className="col-md-6">
<div className="row">
{hourlyForcastData.map(function (hourlyforcast, index) {
if (index < 4 && index > 0) {
return (
<div
className="col-4 box-weather my-auto text-center"
key={index}
>
<ForcastHour data={hourlyforcast} />
</div>
);
}
})}
</div>
</div>
<div className="col-md-6">
<div className="row">
{hourlyForcastData.map(function (hourlyforcast, index) {
if (index < 7 && index > 3) {
return (
<div
className="col-4 box-weather my-auto text-center"
key={index}
>
<ForcastHour data={hourlyforcast} />
</div>
);
}
})}
</div>
</div>
</div>
);
};
export default WeatherHourlyForcast;
I am using React + Firebase store to display images that I have stored in my firebase storage. When I click the logo or "home" navigation button which routes to the page you are already at("/"), the images disappear. The images also disappear when I click a button used to sort the menu.
This is the file that retrieves the images:
let { docs } = useFirestore("images");
// This code is used to display the images/cards as 2 per row
var chunks = function (array, size) {
var results = [];
while (array.length) {
results.push(array.splice(0, size));
}
return results;
};
let data = chunks(docs, 2);
return (
<>
<div class="container">
<div class="row">
<div class="col-sm col-2 mt-5">
</div>
<div class="col-12">
<div className="cards">
<h1 className="cards__header">Menu</h1>
<div className="cards__container">
<div className="cards__wrapper">
{data.map((childs, index) => {
return (
<ul className="cards__items">
{childs.map((c, cindex) => {
return (
<>
<CardItem
src={c.url}
key={c.id}
text={c.imageText}
amountLeft={c.amountLeft}
label={c.imageLabel}
price={c.price}
id={c.id}
desc={c.desc}
cat={c.cat}
></CardItem>
))}
</>
);
})}
</ul>
);
})}
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
};
This is the useFireStore.js file:ยด
import { useState, useEffect } from "react";
import { projectFirestore } from "../Firebase";
const useFirestore = (collection) => {
const [docs, setDocs] = useState([]);
useEffect(() => {
const unsub = projectFirestore
.collection(collection)
.orderBy("createdAt", "desc")
.onSnapshot((snap) => {
let documents = [];
snap.forEach((doc) => {
documents.push({ ...doc.data(), id: doc.id });
});
setDocs(documents);
});
return () => unsub();
}, [collection]);
return { docs };
};
export default useFirestore;
You can see the app at https://save-a-meal.web.app, it is in Danish though.
The bug is in the chunks part. On a rerender it is called again but it manipulates the docs value. To make your code work just call it like this:
const data = chunks([...docs], 2);
insted of:
const data = chunks(docs, 2);
We create a copy of the data so the function doesn't manipulate it.
SOS! Here i have favourite meals from local storage assigned to favs state. I then use my favs to fetch a meal for each favs value. My favs state updates on initial render, but fetchedMeals is not. It updates only after SECOND render. It seems that i have problem somewhere with async function. Would appreciate any help. Will provide with more details immediately if needed.
import React from 'react';
import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import NavBar from '../Home/NavBar';
function Favourites() {
const [favs, setFavs] = useState([]);
const [fetchedMeals, setFetchedMeals] = useState([]);
const getFavsFromLs = () => {
let itemsFromLs = JSON.parse(localStorage.getItem('favs')) || [];
setFavs(itemsFromLs);
};
const fetchMeal = async (mealName) => {
const linkByMealName = `https://www.themealdb.com/api/json/v1/1/search.php?s=${mealName}`;
const res = await fetch(linkByMealName);
const meal = await res.json();
return meal.meals[0];
};
const getFavMeals = async () => {
let items = [];
for (let i = 0; i < favs.length; i++) {
fetchMeal(favs[i]).then((res) => items.push(res));
}
setFetchedMeals(items);
};
useEffect(() => {
getFavsFromLs(); //Moved getFavMeals to another useEffect
}, []);
useEffect(() => {// New use effect that listens favs
if (favs && favs.length) {
getFavMeals();
}
}, [favs]);
return (
<>
<NavBar />
<section className="section">
<div className="row">
<div className="container col l12">
{fetchedMeals.map((meal) => {
const { strMeal, strMealThumb, idMeal } = meal;
return (
<Link to={`/favourites/${strMeal}`}>
<div class="col s12 m6 l3" key={idMeal}>
<div class="card">
<div class="card-image">
<img src={strMealThumb} alt="img"></img>
</div>
<div class="card-content">
<h5 className="truncate center">{strMeal}</h5>
</div>
</div>
</div>
</Link>
);
})}
</div>
</div>
</section>
</>
);
}
export default Favourites;
Since setting state is an asynchronous you need to make useEffect for favs and when they are set from localStorage you can make that getFavMeals and at that point favs will have data.
Also doing console.log immediately after setting state will also show old value, because it needs to rerender. You are trying also on console.log(fetchedMeals).
import React from 'react';
import { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import NavBar from '../Home/NavBar';
function Favourites() {
const [favs, setFavs] = useState([]);
const [fetchedMeals, setFetchedMeals] = useState([]);
const getFavsFromLs = () => {
let itemsFromLs = JSON.parse(localStorage.getItem('favs')) || [];
setFavs(itemsFromLs);
};
const fetchMeal = async (mealName) => {
const linkByMealName = `https://www.themealdb.com/api/json/v1/1/search.php?s=${mealName}`;
const res = await fetch(linkByMealName);
const meal = await res.json();
console.log(meal.meals[0]);
return meal.meals[0];
};
const getFavMeals = async () => {
let items = [];
for (let i = 0; i < favs.length; i++) {
fetchMeal(favs[i]).then((res) => items.push(res));
}
setFetchedMeals(items);
console.log(fetchedMeals);
console.log(items);
};
useEffect(() => {
getFavsFromLs(); //Moved getFavMeals to another useEffect
}, []);
useEffect(() => {// New use effect that listens favs
if (favs && favs.length) {
getFavMeals();
}
}, [favs]);
return (
<>
<NavBar />
<section className="section">
<div className="row">
<div className="container col l12">
{fetchedMeals &&
fetchedMeals.map((meal) => {
console.log(meal);
const { strMeal, strMealThumb, idMeal } = meal;
console.log(meal);
return (
<Link to={`/favourites/${strMeal}`}>
<div class="col s12 m6 l3" key={idMeal}>
<div class="card">
<div class="card-image">
<img src={strMealThumb} alt="img"></img>
</div>
<div class="card-content">
<h5 className="truncate center">{strMeal}</h5>
</div>
</div>
</div>
</Link>
);
})}
</div>
</div>
</section>
</>
);
}
export default Favourites;