Fetched Object from API doesn't appear in mapping - javascript

I call a get request to my api, and then register them to my state with this:
useEffect(() => {
fetchPosts()
},)
const [posts, setPosts] = useState([])
const fetchPosts = async () => {
const data = await fetch('http://localhost:3000/posts/')
const posts_data = await data.json()
setPosts(posts_data)
}
I even tried the axios approach:
await axios.get('http://localhost:3000/posts/')
.then(res => {
setPosts(res.data)
console.log(posts)
})
If I console.log posts_data and posts, it gives me the Object I got from my api:
[{title: "Sample post", desc: "sample desc"}, {...}]
But whenever I iterate and display it:
<div>
{posts.map(post => {
<div>
<p>{post.title}</p>
<h1>asdjasdljaskldjs</h1>
</div>
})}
</div>
It doesn't show up on the page. I even tried adding that random string there asdjasdljaskldjs and it doesn't show too. The data is received and stored, but I wonder why it doesn't display.
Entire component code
import React, {useState, useEffect} from 'react'
import axios from 'axios'
function Posts() {
useEffect(() => {
fetchPosts()
},)
const [posts, setPosts] = useState([])
const fetchPosts = async () => {
await axios.get('http://localhost:3000/posts/')
.then(res => {
setPosts(res.data)
console.log(posts)
})
// const data = await fetch('http://localhost:3000/posts/')
// const posts_data = await data.json()
// setPosts(posts_data)
// console.log(posts)
}
return (
<div className="container-fluid col-lg-7 mt-3">
<h1>POSTS</h1>
<div>
{posts.map(post => {
<div>
<p>{post.title}</p>
<h1>asdjasdljaskldjs</h1>
</div>
})}
</div>
</div>
)
}
export default Posts
I also noticed when I console.log the posts_data or posts, it keeps printing over and over again while you're on the page. Is that normal?

Your mapping function isn't returning the JSX. Change your return to:
return (
<div className="container-fluid col-lg-7 mt-3">
<h1>POSTS</h1>
<div>
{posts.map(post => (
<div>
<p>{post.title}</p>
<h1>asdjasdljaskldjs</h1>
</div>
))}
</div>
</div>
)
You need to surround the returned JSX with parens, not {}, or you need a return before the {}.

Related

Struggling to filter by category properly

import React, { useEffect, useState } from "react";
import Loading from "./Loading";
function App() {
const url = "https://course-api.com/react-tabs-project";
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
async function setCompany(companyName) {
await getData();
const newData = data.filter((info) => info.company === companyName);
setData(newData);
}
async function getData() {
try {
const response = await fetch(url);
const data = await response.json();
setData(data);
setLoading(false);
} catch (err) {
setLoading(false);
console.error(`ERROR ==> ${err}`);
}
}
useEffect(() => {
getData();
}, []);
if (loading) {
return <Loading></Loading>; // simple loading screen
}
return (
<main>
<div className="top-wrapper">
<h2>Experience</h2>
<div className="underline"></div>
</div>
{data.map((item) => {
const { id, order, title, dates, duties, company } = item;
return (
<article key={id}>
<h3>{title}</h3>
<span className="company">{company}</span>
<p>{dates}</p>
<ul>
{duties.map((duty, index) => {
return <li key={index}>{duty}</li>;
})}
</ul>
<button>MORE INFO</button>
</article>
);
})}
<div className="nav-buttons">
<button
onClick={() => {
setCompany("TOMMY");
}}
className="nav-btn"
>
TOMMY
</button>
<button
onClick={() => {
setCompany("BIGDROP");
}}
className="nav-btn"
>
BIGDROP
</button>
<button
onClick={() => {
setCompany("CUKER");
}}
className="nav-btn"
>
CUKER
</button>
</div>
</main>
);
}
export default App;
Sooo... basically I'm trying to filter the array returned by Fetch and have it display only the category I want (I called it "company instead of category in my code") depending on which button I click as shown in the "nav-buttons" div down in the code.
The first time I click on a button it works fine, but the second time it doesn't show anything as if it's filtering from an already filtered array which return no results obviously.
update these two methods with these two lines:
async function setCompany(companyName) {
const response=await getData(); //THIS ONE
const newData = response.filter((info) => info.company === companyName);
setData(newData);
}
async function getData() {
try {
const response = await fetch(url);
const data = await response.json();
setData(data);
setLoading(false);
return data;// And THIS ONE
} catch (err) {
setLoading(false);
console.error(`ERROR ==> ${err}`);
}
}
// Get the oportunity to learn about promises, and you will save so much time. ;)
import React, { useEffect, useState } from "react";
function App() {
const url = "https://course-api.com/react-tabs-project";
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
const [companyName, setCompanyName] = useState("");
async function setCompany(companyName) {
getData();
const newData = setData(newData);
}
function getData(companyName) {
setCompanyName(companyName);
fetch(url)
.then((res) => res.json())
.then((info) => {
console.log(info);
return companyName
? info.filter((info) => info.company == companyName)
: info;
})
.then((res) => {
console.log(res);
return setData(res);
})
.catch((err) => {
setLoading(false);
console.error(`ERROR ==> ${err}`);
});
}
useEffect(() => {
getData();
}, []);
return (
<main>
<div className="top-wrapper">
<h2>Experience</h2>
<div className="underline"></div>
</div>
{data.map((item) => {
const { id, order, title, dates, duties, company } = item;
return (
<article key={id}>
<h3>{title}</h3>
<span className="company">{company}</span>
<p>{dates}</p>
<ul>
{duties.map((duty, index) => {
return <li key={index}>{duty}</li>;
})}
</ul>
<button>MORE INFO</button>
</article>
);
})}
<div className="nav-buttons">
<button
onClick={() => getData("TOMMY")}
className="nav-btn"
>
TOMMY
</button>
<button
onClick={() => getData("BIGDROP")}
className="nav-btn"
>
BIGDROP
</button>
<button
onClick={() => getData("CUKER")}
className="nav-btn"
>
CUKER
</button>
</div>
</main>
);
}
export default App;
you don't need to call the same API on each filter as it returns same data if I'm not wrong.
you can filter the data with the derived state, by storing the selected company in state i.e., on each render it calculates based on the selected company.
use the filtered data to render finally.
Here is the full e.g.
import React, { useEffect, useState } from "react";
import Loading from "./Loading";
function App() {
const url = "https://course-api.com/react-tabs-project";
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
const [selectedCompany, setSelectedCompany] = useState(""); // store the company on click
const filteredData = selectedCompany ? data.filter(info=> info.company === selectedCompany) : data; // filter data based on selected company
async function getData() {
try {
const response = await fetch(url);
const data = await response.json();
setData(data);
setLoading(false);
} catch (err) {
setLoading(false);
console.error(`ERROR ==> ${err}`);
}
}
useEffect(() => {
getData();
}, []);
if (loading) {
return <Loading></Loading>; // simple loading screen
}
return (
<main>
<div className="top-wrapper">
<h2>Experience</h2>
<div className="underline"></div>
</div>
{filteredData.map((item) => {
const { id, order, title, dates, duties, company } = item;
return (
<article key={id}>
<h3>{title}</h3>
<span className="company">{company}</span>
<p>{dates}</p>
<ul>
{duties.map((duty, index) => {
return <li key={index}>{duty}</li>;
})}
</ul>
<button>MORE INFO</button>
</article>
);
})}
<div className="nav-buttons">
<button
onClick={() => {
setSelectedCompany("TOMMY");
}}
className="nav-btn"
>
TOMMY
</button>
<button
onClick={() => {
setSelectedCompany("BIGDROP");
}}
className="nav-btn"
>
BIGDROP
</button>
<button
onClick={() => {
setSelectedCompany("CUKER");
}}
className="nav-btn"
>
CUKER
</button>
</div>
</main>
);
}
export default App;
try putting a check case before filter to insure that your array isn't empty.
async function setCompany(companyName) {
await getData();
{data ?
const newData = data.filter((info) => info.company === companyName);
:
null}
setData(newData);
}
I think part of your issue is when your calling get data on button click your state isn't set before running the filter logic. I would look over your functional logic and ask yourself is this the best way to do this and am i trying to filter before or after my response.

Not able to Map over Fetch data

I am not able to map over the data which is stored in the redux array.
This action creator fetches the data and calls the dispatch action.
import { checkStatus, getTours, checkLoading } from "../../features/tourSlice";
export const getData = () => {
return async (dispatch) => {
try {
dispatch(checkLoading(true));
const res = await fetch(`http://localhost:8080/backpack/api/r1/tours`);
if (!res.ok) {
throw new Error(`Error while connecting with server`);
}
const data = await res.json();
dispatch(checkLoading(false));
dispatch(getTours(data));
} catch (error) {
dispatch(
checkStatus({
title: `Error`,
message: `Servers are down or Error while connecting please try again later`,
})
);
}
};
};
calling in app.js
function App() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getData());
}, [dispatch]);
while using this component gives nothing.
const data = useSelector((state) => state.Tour.tours);
but doing console.log of data gives the array but gives nothing when maping over
small example of mapping=>
<main className="main">
<div className="card-container">
{data.map((item) => (
<div className="card" key={item._id}>
{console.log(item)}
<div className="card__header">
<div className="card__picture">
<div className="card__picture-overlay"> </div>
<img
src=`${item.image}`
alt="Tour 1"
className="card__picture-img"
/>

My component doesn't render for some reason

I'm trying to make sort of a twitter clone using react and in the Feed component, I am not able to render the tweets for some reason.
This is Home, the parent component of Feed. this is also where I call most of the relevant info from firebase.
function Home() {
const[userInfo, setUserInfo] = useState({})
const {user} = useUserAuth()
useEffect(() => {
const getUser = async()=>{
const docRef = doc(db, "users", auth.currentUser.uid);
const docSnap = await getDoc(docRef);
if (docSnap.exists()) {
setUserInfo(docSnap.data())
} else {
// doc.data() will be undefined in this case
}}
getUser()
},[user]);
return (
<div className='cont-all'>
<div className='menu'>
<IconContext.Provider value={{className:"react-icons"}}>
<ul>
<li className='menu-option'><GrTwitter color='rgb(14, 147, 255)' size={30}/></li>
<li className='menu-option'><div className='hover-wrap'><AiFillHome size={23} ></AiFillHome>{window.innerWidth > 1200? "Home":null}</div></li>
<li className='menu-option'><div className='hover-wrap'><BiMessageAltDetail size={23}></BiMessageAltDetail>{window.innerWidth > 1200? "Messages":null}</div></li>
<li className='menu-option'><div className='hover-wrap'><AiOutlineUser size={23}></AiOutlineUser>{window.innerWidth> 1200? "Profile":null}</div></li>
</ul>
</IconContext.Provider>
<SessionInfo user ={userInfo}/>
</div>
<div className='feed'>
<CreateTweet username={userInfo.username} />
{userInfo?<Feed user_info={userInfo}/>:null}
</div>
<div className='trends'>3</div>
</div>
)
}
This is the Feed component, parent of the Tweet components i'm trying to render multiple times depending on the number of posts i have stored.I'm storing the props in state in the first useEffect and the second useEffect is where I call all the tweets from firebase
function Feed({ user_info }) {
const [tweets, setTweets] = useState([]);
const [user,setUser] = useState({})
useEffect(()=>{
setUser(user_info)
},[user_info])
useEffect(() => {
const tweets_arr = []
if (user.posts!==undefined && user.posts!==[]){
const call = async (post)=>{
const docRef = doc(db, "Twoots", post);
const wanted_doc = await getDoc(docRef);
tweets_arr.push(wanted_doc.data())
}
user.posts.forEach(element => {
call(element)
});
setTweets(tweets_arr)
}
},[user]);
return (
<div>
{tweets.map(item =>{return <Tweet item={item} />})}
</div>
);
}
Finally this is the Tweet component. I don't think it's too relevant as it just receives data and displays it but i'll leave it here anyway
function Tweet(props) {
return (
<div className='tweet'>
<div className='wrap-pfp-twt'>
<div className='tw-pfp-cont1'><img src='https://conteudo.imguol.com.br/c/esporte/96/2021/11/29/lionel-messi-atacante-do-psg-1638213496667_v2_4x3.jpg' className='tw-pfp' alt=""></img></div>
<div>
<div className='author-id'>#{props.item.username} . {props.item.likes}</div>
<div className='actual-tweet'>{props.item.body}</div>
</div>
</div>
<div className='interaction-bar'>
<FaRegComment/><BsShare/><AiOutlineHeart/>
</div>
</div>
)
}
Your tweets array is always going to be empty.
If you formatted your code correctly, this is what you'd see:
useEffect(() => {
const tweets_arr = []
if (user.posts!==undefined && user.posts!==[]){
const call = async (post)=> {
const docRef = doc(db, "Twoots", post);
const wanted_doc = await getDoc(docRef);
tweets_arr.push(wanted_doc.data())
}
user.posts.forEach(element => {
call(element)
});
setTweets(tweets_arr)
}
}, [user]);
Your problem is that the call() function is async. When you do a .forEach it does not wait for the async function to complete, so it immediately calls setTweets(tweets_arr). And since the .forEach has not completed, you will never see the tweets

How do I target each image seperately, and fetch API data from them, instead of collect it all at once

I've created an app that connects to an API that retrieves dog images. On page load, 12 images are displayed, along with json text, that provides information about the breeds; height of the dog etc.
My final step would be somehow connecting the a button (which already exists) to each individual image, then retrieving data for that one specific dog/image after clicking it, instead of the API fetching all of the data at once on initial page load.
App.js
import './App.css';
import './Dog.js';
import './index.css';
import FetchAPI from './FetchAPI';
function DogApp() {
return (
<div className="dogApp">
<FetchAPI />
</div>
);
}
export default DogApp;
FetchAPI.js
import React, { useState, useEffect } from 'react'
const FetchAPI = () => {
const [data, setData] = useState([]);
const apiGet = () => {
const API_KEY = "";
fetch(`https://api.thedogapi.com/v1/images/search?limit=12&page=10&order=Desc?API_KEY=${API_KEY}`)
.then((response) => response.json())
.then((json) => {
console.log(json);
//setData([...data,json]); if json is single object
setData([...data, ...json]); // if json is array of one object then use this line
});
};
useEffect(() => { //call data when pagee refreshes/initially loads
apiGet();
}, []);
return (
<div>
{data.map((item) => (
<div class="dog">
<img src={item.url}></img>
<button onClick={item.breeds}>Fetch API</button>
</div>
))}
{data.map((item) => (
<p>{JSON.stringify(item.breeds)}</p>
))}
{/*<pre>{JSON.stringify(data, null, 2)}</pre> */}
<br />
</div>
)
}
export default FetchAPI;
Make an other function which will fetch new (single) image and change it to the state as I have made function named apiGetSingle which changes the data on specific index. And if you have made the route as I have mentioned in apiGetSingle which will return single new image then it will work fine otherwise made backend route for that too.
import React, { useState, useEffect } from 'react'
const FetchAPI = () => {
const [data, setData] = useState([]);
const apiGet = () => {
const API_KEY = "";
fetch(`https://api.thedogapi.com/v1/images/search?limit=12&page=10&order=Desc?API_KEY=${API_KEY}`)
.then((response) => response.json())
.then((json) => {
console.log(json);
//setData([...data,json]); if json is single object
setData([...data, ...json]); // if json is array of one object then use this line
});
};
const apiGetSingle = (index) => {
const API_KEY = "";
fetch(`https://api.thedogapi.com/v1/images/search?API_KEY=${API_KEY}`)
.then((response) => response.json())
.then((json) => {
console.log(json);
let d=[...data];
d[index]=json; // if json is single object.
d[index]=json[0] // if json returns array
setData(d);
};
useEffect(() => {
}, []);
return (
<div>
{data.map((item,index) => (
<div class="dog">
<img src={item.url}></img>
<button onClick={()=>apiGetSingle(index)}>Fetch API</button>
</div>
))}
{data.map((item) => (
<p>{JSON.stringify(item.breeds)}</p>
))}
<button onClick={apiGet}>Fetch API</button>
{/*<pre>{JSON.stringify(data, null, 2)}</pre> */}
<br />
</div>
)
}
export default FetchAPI;

Bug in custom hook for fetching local json

I am not able to find where is the issue with this custom hook?
import { useState, useEffect } from "react";
const SAMPLE_DATA_URL = "../feed/sample.json";
const useFetch = () => {
const [response, setResponse] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
const doFetch = async () => {
setLoading(true);
await fetch(SAMPLE_DATA_URL)
.then(res => res.json())
.then(jsonData => {
setResponse(jsonData);
})
.catch(err => setError(err))
.finally(() => setLoading(false));
};
doFetch();
},[]);
return { response, error, loading };
};
export default useFetch;
on network tab I can see 200 OK but the preview is saying "You need to enable JavaScript to run this app." and also the response is html of my index screen. I checked javascript in browser is allowed and the json file is a valid json.
on return object I am getting error: true
Where its been used
import React from "react";
import styles from "./Series.module.css";
import { TitleBar } from "../../atoms";
import {useFetch} from '../../utils';
const Series = () => {
const { response, loading, error } = useFetch();
return (
<div >
<TitleBar>Popular Series</TitleBar>
<div className={styles.content}>
{loading && <p>Loading...</p>}
{error && <p>Oops, Something went wrong...</p>}
{response && <p>response</p>}
</div>
</div>
);
};
export default Series;
If you are using CRA, you can put your sample.json inside your public folder and so you can fetch the URL directly:
fetch("sample.json")
.then(...)
.then(...)
Although, you don't need to do all that as you can just import the data like any other js modules
import data from "./sample.json"; // Path
const App = () => {
return (
<div className="App">
{data.map(item => {
// return JSX with item...
})}
</div>
);
};
codesandbox examples.

Categories