I have this simple React examle. Show data in React from API:
import React, { useEffect, useState } from "react";
const UsingFetch = () => {
const [users, setUsers] = useState([])
const fetchData = () => {
console.log('fetching');
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => {
return response.json()
})
.then(data => {
console.log(data);
setUsers(data)
})
}
useEffect(() => {
fetchData()
}, [])
return (
<div>
{users.length > 0 && (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)}
</div>
)
}
export default UsingFetch
Why is in console twice: fetching and data from console.log?
Related
I am trying to set an array dynamically and render it using useState hook. But it seems the array is not setting. below is my code:
import React, { useState, useEffect } from "react";
export default ({ item }) => {
const [attachments, setAttachments] = useState([]);
const setAttachmentValues = function(response){
setAttachments(response.data);
}
const fetchMedia = async ()=> {
setAttachments([]);
await apiCall().then((response) => {
setAttachmentValues(response);
});
}
useEffect(() => {
fetchMedia();
}, []);
return (
<>
<div className="w-full">
{(attachments.map((ele) => {
<div>{ele}</div>
)}
</>
)
}
apiCall() will return an array of objects.
setState in this way is working well in some cases. What is the actual issue here?
This way you can render data
import React, { useState, useEffect } from 'react';
export default ({ item }) => {
const [attachments, setAttachments] = useState([]);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.then((response) => {
setAttachments(response);
console.log(response);
});
}, []);
return (
<>
<div>
{attachments.map(item => <div key={item.username}> {item.username} </div> )}
</div>
</>
);
};
I want to display the response from my fetch request in react. So far, I built the fetch request and set up the useEffect and useState hooks. The response is an object. What am I doing wrong?
function App() {
const url = 'https://api.gemini.com/v1/book/btcusd'
const [orders, setOrders] = useState([])
const fetchData = () => {
fetch(url).then(response => {
return response.json();
}).then(data => {
console.log(data)
setOrders(data)
}).catch(error => {
console.log(error)
})
}
useEffect(() => {
fetchData()
}, [])
return (
<div className="App">
<h1>{orders.asks}</h1>
</div>
);
}
export default App;
Taking a quick look at that API, the asks property holds an array of objects. You would need to map those to JSX elements in order to display them.
Also, if orders is meant to be an object, you should not initialise it as an array.
Finally, you should always check the Response.ok property to see if the request resolved successfully.
// fetch-data.js
const url = "https://api.gemini.com/v1/book/btcusd";
export const fetchData = async () => {
const res = await fetch(url);
if (!res.ok) {
throw Object.assign(new Error(`${res.status}: ${res.statusText}`), {
url,
text: await res.text(),
});
}
return res.json();
};
// App.jsx
import { useEffect, useState } from "react";
import { fetchData } from "./fetch-data";
function App() {
// init with an object with empty `asks` array
const [orders, setOrders] = useState({ asks: [] });
useEffect(() => {
fetchData().then(setOrders).catch(console.error);
}, []);
return (
<div className="App">
{/* Map over the data */}
{orders.asks.map(({ price, amount }, i) => (
<dl key={i}>
<dt>Price</dt>
<dd>{price}</dd>
<dt>Amount</dt>
<dd>{amount}</dd>
</dl>
))}
</div>
);
}
export default App;
Your data is an object. You should use map to loop.
const url = "https://api.gemini.com/v1/book/btcusd";
const [orders, setOrders] = useState({ asks: [{price: 0, amount: 0}], bids: [{price: 0, amount: 0}] });
const fetchData = () => {
fetch(url)
.then((response) => {
return response.json();
})
.then((data) => {
console.log(data); //Data: {bids: Array(50), asks: Array(50)}
setOrders(data);
})
.catch((error) => {
console.log(error);
});
};
useEffect(() => {
fetchData();
}, []);
return (
<div className="App">
<table className="table">
<thead>
<th>Ask price</th>
<th>Ask amount</th>
<th>Bid price</th>
<th>Bid amount</th>
</thead>
{orders.asks?.map((e, i) => {
return (
<>
<tr>
<td>{e.price}</td>
<td>{e.amount}</td>
<td>{orders.bids[i].price}</td>
<td>{orders.bids[i].amount}</td>
</tr>
</>
)
})}
</table>
</div>
);
You Can use map function to display each item
eg:
orders.asks.map(item=>
<div>
<h1>{item.price}</h1>
<h1>{item.amount}</h1>
</div>
)
I'm practising React with a small project where I want to display some Nba players but I don't get any data when trying to map an object.
I'm using this Api: http://data.nba.net/prod/v1/2022/players.json
Here is the code:
import React, { useEffect, useState } from "react";
const Players = () => {
const url = "http://data.nba.net/prod/v1/2022/players.json";
const [players, setPlayers] = useState([]);
useEffect(() => {
getPlayers();
}, []);
const getPlayers = async () => {
const api = await fetch(url);
const data = await api.json();
//wrapping a object into a array
setPlayers([data].flat());
};
return (
<div>
<h3>Sacramento player info</h3>
<ul>
{players.map((player) => (
<li key={player.league.sacramento.id}>
{player.league.sacramento.firstName}{" "}
{player.league.sacramento.lastName}{" "}
</li>
))}
</ul>
</div>
);
};
export default Players;
I recreated your code on codesandbox and it works just fine. I use other approach on getting data thru fetch and changed http:// to https://
const Players = () => {
const [data, setData] = useState(null);
function getAPIData() {
fetch("https://data.nba.net/prod/v1/2022/players.json")
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error("ERROR (response not ok)");
})
.then((data) => {
setData(data);
})
.catch((response) => {
console.log("error");
});
}
useEffect(() => getAPIData(), []);
return (
data && (
<div>
<h3>Sacramento player info</h3>
<ol>
{data.league.sacramento.map((player) => (
<li key={player.personId}>
{player.firstName} {player.lastName}
</li>
))}
</ol>
</div>
)
);
};
working code: https://codesandbox.io/s/players-info-51gf1w
I am trying to setup infinite scrolling using React Hooks, I am getting the data correctly from the node backend (3 dataset per request), and when I scroll new data is also added correctly in the array (in the loadedPlaces state), but the page is going back to top on re render, I need to prevent this behavior. How do I prevent this, and below is my code
import React, { useEffect, useState } from "react";
import "./App.css";
function App() {
const [page, setPage] = useState(1);
const [loadedPlaces, setLoadedPlaces] = useState([]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const getPlaces = async () => {
try {
setIsLoading(true);
const url = `http://localhost:5000/api/places/user/${id}?page=${page}&size=3`;
const responseData = await fetch(url);
const data = await responseData.json();
console.log(data);
setLoadedPlaces((prev) => [...prev, ...data.places]);
setIsLoading(false);
} catch (error) {}
};
getPlaces();
}, [page]);
window.onscroll = function (e) {
if (
window.innerHeight + document.documentElement.scrollTop ===
document.documentElement.offsetHeight
) {
setPage(page + 1);
}
};
return (
<div className='App'>
<h1>Infinite Scroll</h1>
{!isLoading &&
loadedPlaces.length > 0 &&
loadedPlaces.map((place, index) => (
<div key={index} className={"container"}>
<h1>{place.location.lat}</h1>
<h1>{place.location.lng}</h1>
<h1>{place.title}</h1>
<h1>{place.description}</h1>
<h1>{place.address}</h1>
<hr></hr>
</div>
))}
</div>
);
}
export default App;
Any help is highly appreciated
This is happening because whenever you scroll you are calling
window.onscroll = function (e) {
if (
window.innerHeight + document.documentElement.scrollTop ===
document.documentElement.offsetHeight
) {
setPage(page + 1);
}
};
And it's changing the page count and that changed page count leads to again run the
useEffect(() => {
const getPlaces = async () => {
try {
setIsLoading(true);
const url = `http://localhost:5000/api/places/user/${id}?page=${page}&size=3`;
const responseData = await fetch(url);
const data = await responseData.json();
console.log(data);
setLoadedPlaces((prev) => [...prev, ...data.places]);
setIsLoading(false);
} catch (error) {}
};
getPlaces();
}, [page]);
and in that function, you are doing setIsLoading(true) so that it is again rendering this because of
{!isLoading && <-----
loadedPlaces.length > 0 &&
loadedPlaces.map((place, index) => (
<div key={index} className={"container"}>
<h1>{place.location.lat}</h1>
<h1>{place.location.lng}</h1>
<h1>{place.title}</h1>
<h1>{place.description}</h1>
<h1>{place.address}</h1>
<hr></hr>
</div>
))}
And that leads you to the top of the page
You can try this approach:
import React, { useEffect, useState } from "react";
import "./App.css";
function App() {
const [page, setPage] = useState(1);
const [loadedPlaces, setLoadedPlaces] = useState([]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const getPlaces = async () => {
try {
setIsLoading(true);
const url = `http://localhost:5000/api/places/user/${id}?page=${page}&size=3`;
const responseData = await fetch(url);
const data = await responseData.json();
console.log(data);
setLoadedPlaces((prev) => [...prev, ...data.places]);
setIsLoading(false);
} catch (error) {}
};
getPlaces();
}, [page]);
window.onscroll = function (e) {
if (
window.innerHeight + document.documentElement.scrollTop ===
document.documentElement.offsetHeight
) {
setPage(page + 1);
}
};
return (
<div className='App'>
<h1>Infinite Scroll</h1>
{
loadedPlaces.length > 0 &&
loadedPlaces.map((place, index) => (
<div key={index} className={"container"}>
<h1>{place.location.lat}</h1>
<h1>{place.location.lng}</h1>
<h1>{place.title}</h1>
<h1>{place.description}</h1>
<h1>{place.address}</h1>
<hr></hr>
</div>
))}
</div>
);
}
export default App;
You can add this.
function ScrollToBottom(){
const elementRef = useRef();
useEffect(() => elementRef.current.scrollIntoView());
return <div ref={elementRef} />;
};
And then:
return (
<div className='App'>
<h1>Infinite Scroll</h1>
{!isLoading &&
loadedPlaces.length > 0 &&
loadedPlaces.map((place, index) => (
<div key={index} className={"container"}>
<h1>{place.location.lat}</h1>
<h1>{place.location.lng}</h1>
<h1>{place.title}</h1>
<h1>{place.description}</h1>
<h1>{place.address}</h1>
<hr></hr>
</div>
))}
<ScrollToBottom />
</div>
);
Having done the necessary to read the data using fetchAPI, I am having problems displaying the content because of the nature of the array.
import React, { useState, useEffect } from "react";
function Home() {
const [userData, setUserData] = useState([]);
async function getData() {
let response = await fetch("https://api.xxxxxxxx.io/something/students");
let data = await response.json();
return data;
}
//call getData function
getData().then((data) => console.log(data)); //
useEffect(() => {
getData()
.then((data) => {
setUserData(data);
})
.catch((error) => {
console.log(error);
});
}, []);
return (
<div>
{Object.keys(userData).map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);
}
export default Home;
When I checked the console, the data are displayed but it is only showing students with no other data displayed.
I have the data below.
Try the following changes:
const [userData, setUserData] = useState({ students: [] });
...
return (
<div>
{userData.students.map((item, index) => (
<div key={index}>{item}</div>
))}
</div>
);