UseEffect API Request not displaying on page load - javascript

Apologies in advance if similar questions like this have already been answered. I have tried everything, but still cannot figure out why I am experiencing this small bug.
I want this collection of tweets from my Firestore to render on the page when it loads. Right now, it only happens after I make a post request.
This is my request
useEffect(() => {
const getTweets = async () => {
const tweets = await firestore.collection('tweet').get();
tweets.forEach(doc => {
results.push(doc.data());
})
}
getTweets()
}, [])
This is where I'm mapping it to the page:
return (
<>
<main className="tweet-container">
<div className="tweet-container--content">
{results.map((tweet, index) => (
<InputResults
key={index}
tweet={tweet}
/>
))}
</div>
</main>
</>
)
}
Thank you so much

Try something like this,
import React, {useState, useEffect} from "react";
function App() {
const [results, setResults] = useState([]);
useEffect(() => {
const getTweets = async () => {
const tweetsData = [];
const tweets = await firestore.collection('tweet').get();
tweets.forEach(doc => {
tweetsData.push(doc.data());
})
setResults(tweetsData);
}
getTweets()
}, [])
return (
<>
<main className="tweet-container">
<div className="tweet-container--content">
{results.map((tweet, index) => (
<h1>{tweet}</h1>
))}
</div>
</main>
</>
)
}
Reference: https://reactjs.org/docs/hooks-state.html

Always add a key property to the elements when used a map function to show them on UI .
As it would help react to differentiate between the elements .
return (
<>
<main className="tweet-container">
<div className="tweet-container--content">
{results.map((tweet, index) => (
<h1 key={index} >{tweet}</h1>
))}
</div>
</main>
</>
)

Related

Problem displaying an item according to the url - React

I have a question. I have a component that when entering /category/:categoryId is rendered doing a fecth to "api url + categoryId". My problem is that if I change from one category to another the page only changes if the useEffect is executed infinitely which generates problems to the view as seen below. How can I make it run once and when I change from /category/1 to /category/2 the useEffect is executed correctly?
const Categories = () => {
let [producto, productos] = useState([]);
const { categoryId } = useParams();
useEffect(() => {
fetch('https://fakestoreapi.com/products/category/' + categoryId)
.then(res=>res.json())
.then(data=>productos(data))
},[]);
console.log(producto)
return(
<div className="container">
{producto.map((p) => (
<Producto
title={p.title}
price={p.price}
description={p.description}
image={p.image}
key={p.id}
id={p.id}
/>
))}
</div>
)
}
export default Categories;
My router file:
<Route path="/category/:categoryId" component={Categories} />
This is the problem that is generated, there comes a time when a fetch is made to a previously requested category and then the new requested category is executed.
See my problem in video
You can simply add categoryId to useEffect array argument. Function inside the useEffect is called, only when categoryId changes
useEffect(() => {
fetch('https://fakestoreapi.com/products/category/' + categoryId)
.then(res=>res.json())
.then(data=>productos(data))
},[categoryId]);
you can not edit producto directly, you should use productos :
const Categories = () => {
let [producto, productos] = useState([]);
const { categoryId } = useParams();
useEffect(() => {
fetch('https://fakestoreapi.com/products/category/' + categoryId)
.then(res=>res.json())
.then(data=>productos(data))
},[]);
console.log(producto)
return(
<div className="container">
{producto && producto.map((p) => (
<Producto
title={p.title}
price={p.price}
description={p.description}
image={p.image}
key={p.id}
id={p.id}
/>
))}
</div>
)
}
export default Categories;

React Hooks: Loading state displaying twice

I have an isLoading useState variable. In the code below purposefully commented out the isLoading(false) to force the Loading UI to test it. However I'm seeing it render twice, any idea why?
API Call
export default function App() {
const [upcoming, setUpcoming] = useState([]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const fetchUpcoming = async () => {
setIsLoading(true);
const res = await fetch(
"https://api.themoviedb.org/3/movie/upcoming?api_key={API_KEY}&language=en-US&page=1"
);
const data = await res.json();
const results = data.results;
setUpcoming(results);
// setIsLoading(false);
};
fetchUpcoming();
}, []);
return (
<div className="App">
<Recommendations title={"Upcoming"} data={upcoming} loading={isLoading} />
</div>
);
}
Render Results
export default function Recommendations({ title, data, loading }) {
return (
<div className="recommendationSection">
<h3>{title}</h3>
{loading ? (
<h3>Loading...</h3>
) : (
data.map((movie) => {
return (
<div className="banner" key={movie.title}>
<img
src={
movie.poster_path
? `https://image.tmdb.org/t/p/original/${movie.poster_path}`
: "https://www.genius100visions.com/wp-content/uploads/2017/09/placeholder-vertical.jpg"
}
alt={movie.title}
/>
</div>
);
})
)}
</div>
);
}
Found the answer here: https://github.com/kenwheeler/slick/issues/940#issuecomment-181815974
I found the second instance had a class of slick-cloned and basically since i'm using Slick Slider, it's making a clone of my element. The fix is to add infinite: false in my slider settings.

How to fetch nested data in react

Question regarding fetching nested data in react.
APIs
https://jsonplaceholder.typicode.com/posts
https://jsonplaceholder.typicode.com/posts/${postId}/comments
Able to fetch list of posts. now want to fetch list of comments from when click on post
here is code so far
import React, { useEffect, useState } from "react";
import Post from "./Post";
const [posts, setPosts] = useState([]);
const [comments, setComments] = useState([]);
function App() {
const [posts, setPosts] = useState([]);
const [comments, setComments] = useState([]);
useEffect(() => {
const loadposts = async() => {
const resp = await fetch("https://jsonplaceholder.typicode.com/posts?userId=1");
const data = await resp.json();
setPosts(data);
}
loadposts();
}, []);
return (
<div className="App">
<ul>
{posts.map((post) =>
(
<div>
<li key={post.id}>
<Post
userId={post.id}
title={post.title}
body={post.body}
/>
</li>
</div>
))
}
</ul>
</div>
);
}
export default App;
function Post({title, body, postId}) {
return (
<div>
<h5>{postId}</h5>
<h1>{title}</h1>
<p>{body}</p>
</div>
)
}
export default Post
appreciate any help. thanks
Firstly, the "/posts" endpoint returns posts by users, so the query "/posts?userId=1" will return all the posts by user id 1. You mistakenly passed a userId prop to the Post component instead of the specific post's id, i.e.
<Post userId={post.id} title={post.title} body={post.body} />
The React key should also be placed on the outer-most element being mapped, the div in your case, but since li is already a block level element the div is basically extraneous.
<ul>
{posts.map((post) => (
<li key={post.id}> // <-- remove div and place React key on li
<Post
postId={post.id} // <-- pass the post's id
title={post.title}
body={post.body}
/>
</li>
))}
</ul>
In Post component create a fetch comments utility and click handler, and attach the click handler to the title header. Conditionally render the comments. If it wasn't already clear, you'll move the comments state into Posts so each post component maintains its own copy. The following is an example for rendering out the comments once fetched, you can use whatever conditional rendering and field subset of your choosing.
const fetchComments = async (postId) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts/${postId}/comments`
);
return response.json();
};
function Post({ title, body, postId }) {
const [comments, setComments] = useState([]);
const clickHandler = () => {
fetchComments(postId).then(setComments);
};
return (
<div>
<h5>{postId}</h5>
<h1 onClick={clickHandler}>{title}</h1>
<p>{body}</p>
{comments.length && (
<>
Comments:
<ul>
{comments.map(({ id, email, name, body }) => (
<li key={id}>
<dl>
<dt>{email} - {name}</dt>
<dd>{body}</dd>
</dl>
</li>
))}
</ul>
</>
)}
</div>
);
}
Working solution if anyone looking for
function Post() {
const {id} = useParams();
const [comments, setComments] = useState([]);
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/posts/${id}/comments`)
.then((res) => res.json())
.then(setComments)
.catch((error) => {
console.log(error)
})
console.log("setComments: ", setComments)
}, [])
return (
<div>
{comments && comments.map((comment) => (
<div key={comment.id}>
<p>{comment.body}</p>
</div>
))}
</div>
)
}
export default Post
then update rendering
<div className="App">
<Switch>
<Route exact path='/'>
{posts.map((post) => (
<article key={post.id}>
<h1>{post.title}</h1>
<Link to ={`/${post.id}`}>
<p>{post.body}</p>
</Link>
</article>
))}
</Route>
<Route path ='/:id'>
<Post/>
</Route>
</Switch>
</div>

Why useEffect runs every time when component re-render?

In my Home component(I call it Home Page!) I am using Cards.JS component which has posts attribute as shown in following code.
const Home = () => {
const dispatch = useDispatch()
const isLoading = useSelector(state => state.isLoading)
const currentPage = useSelector((state) => state.idFor.currentPageHome)
const homePosts = useSelector((state) => state.posts)
useEffect(() => {
dispatch(setIsLoading(true))
dispatch(getAllPosts(currentPage))
}, [dispatch, currentPage])
return (
isLoading ? (
<Loader type="ThreeDots" color="#000000" height={500} width={80} />
) : (
<Cards posts={homePosts} setCurrentPage={setCurrentPageHome} currentPage={currentPage} pageName={"LATEST"} />
)
)
}
And Cards.Js is as following
const Cards = ({ posts, setCurrentPage, currentPage, pageName }) => {
console.log('Cards.JS called', posts);
const dispatch = useDispatch()
useEffect(() => {
dispatch(setIsLoading(false))
})
const handleNextPage = () => {
dispatch(setIsLoading(true))
dispatch(setCurrentPage(currentPage + 1))
}
const handlePreviousPage = () => {
dispatch(setIsLoading(true))
dispatch(setCurrentPage(currentPage - 1))
}
return (
<div className="container">
<h4 className="page-heading">{pageName}</h4>
<div className="card-container">
{
posts.map(post => <Card key={post._id} post={post} />)
}
</div>
<div className="page-div">
{currentPage !== 1 ? <span className="previous-page" onClick={handlePreviousPage}><</span>
: null}
<span className="next-page" onClick={handleNextPage}>></span>
</div>
</div>
)
}
My Problem:
When i come back to home page useEffect is called everytime and request same data to back-end which are already avaliable in Redux store.
Thanks in Advance :)
useEffect will run every time the component rerenders.
However, useEffect also takes a second parameter: an array of variables to monitor. And it will only run the callback if any variable changes in that array.
If you pass an empty array, it will only run once initially, and never again no matter how many times your component rerenders.
useEffect(() => {
dispatch(setIsLoading(false))
}, [])

Fetch request in React: How do I Map through JSON array of object inside of array of objects?

I managed to fetch API and could output some data in browser, but I couldn't handle an array of object in JSON. It's a rest country API, where some countries have more than 1 language. I want to output all languages they speak. Here is the API link.
And here is my code
import React, { useState, useEffect } from "react";
import CountryListCard from "./CountryListCard";
import "./CountryList.scss";
export default function CountryList() {
const [data, setData] = useState([]);
const fetchData = () => {
fetch("https://restcountries.eu/rest/v2/all")
.then((res) => res.json())
.then((result) => setData(result))
.catch((err) => console.log("error"));
};
useEffect(() => {
fetchData();
}, []);
return (
<div>
{data &&
data.map((element, index) => (
<CountryListCard
image={element.flag}
name={element.name}
key={index}
region={element.region}
population={element.population}
{/* language={element.languages[0]} this doesn't work*/}
/>
))}
{/* {data.languages &&
data.languages.map((element, index) => (
<CountryListCard key={index} language={element.languages.iso639_1} /> this doesn't work
))} */}
</div>
);
}
you should call the languages map inside your country map like:
countries.map(country=>(
<div key={country.name}>
<h1>{country.name}</h1>
{country.languages.map((language, languageIndex)=>(
<p key={languageIndex}>{language.name}</p>
))}
</div>
))
Also, it is not related with the post, but I'll suggest you to not use generic names in your .map like item/element/obj
This should work:
{
data.languages &&
data.languages.map((element) =>
element.languages.map((language, index) => (
<CountryListCard key={index} language={language.iso639_1} />
))
);
}
#Greg! This is how it works with Axios and if you will not reuse CountryListCard it could be in the same file as CountryList.
import React, { useState, useEffect } from "react";
import Axios from "axios";
export default function CountryList() {
const [data, setData] = useState([]);
const fetchData = async () => {
try {
const countries = await Axios.get("https://restcountries.eu/rest/v2/all");
setData(countries.data);
} catch (err) {
console.log(err);
}
};
useEffect(() => {
fetchData();
}, []);
return (
<div>
{data?.map((country, i) => (
<CountryListCard
name={country.name}
flag={country.flag}
key={i}
region={country.region}
population={country.population}
languages={country.languages}
alphaCode={country.alpha3code}
/>
))}
</div>
);
}
function CountryListCard(props) {
const [country /*In this case we don't need setCountry*/] = useState(props);
return (
<div id={country.alphaCode} className="country">
<h3>
<img src={country.flag} width="18" alt={country.alphaCode} /> {country.name}
</h3>
<b>Region</b>: {country.region} <br />
<b>Population</b>: {country.population} <br />
<b>Languages</b>:{" "}
{country.languages?.map((lang, i) => (
<li key={i}>{lang.name}</li>
))}
</div>
);
}
The code above will look like this: View result

Categories