problem while extracting data from api in react - javascript

I have created an API to access data from the database. I have fetched the api data using axios and I want to display it. I have mapped the data, but it returns empty array for the first time.
const ExpenseList = () => {
const [result, setResult] = useState([]);
const expenseDetails = async () => {
try {
let res = await axios.get("http://127.0.0.1:8000/expense");
let result = res.data;
setResult(result);
} catch (e) {
console.log(e);
}
};
useEffect(() => {
expenseDetails()
}, []);
console.log("result", result)
return (
<Container className='list-group'>
<Row className='title-row'>
<Col className='title-col-expenses'>Expenses</Col>
</Row>
{
result.map((items)=>{
<ExpenseItem id={items.id} name={items.name} cost={items.cost} />
})}
</Container>
)
}
I have attached a screenshot of console where I have logged "result"
Click to view image

as CevaComic said you are setting the initial value as an empty array.
useEffect will only work after the component has been rendered, so when you will console.log the data stored in result you will get the initial value.
Only after the component will render for the second time, because of the changed made inside setResult, the data from the api will be logged.

Related

Fetched data from database not displaying in FlatList

Exactly as the title says I am fetching data from a database and trying to display the stored image URI as an image in my React Native application. I'm not entirely sure where the process is going wrong, but my array is being filled and nothing is being shown. I have tried hardcoding the data that's being fetched into my application and it is being displayed then. I have wrapped my view statement that the flatlist is nested into with the following: {arrayName && .... }. Here is some relevant code. The Test field appears, but the flatlist will not.
const [data, setData] = React.useState([]);
const userImgData = [];
{userImgData && <View style={{width: 300, height: 300}}>
<FlatList
data={userImgData}
keyExtractor={(item) => item.id}
key={item=> item.id}
renderItem={({ item }) => (
//<Text style={styles.headerText}>{item.id}</Text>
<Image source={{ uri: item.imageURI}} style={{ width: 200, height: 200 }} />
)}
/>
<Text>Test</Text>
</View>}
My array is set here...
function getData(){
axios.get('IP/imagefiles')
.then((response) => {
const myObjects = response.data;
setData(myObjects);
});
//console.log(data);
for(let i = 0; i < data.length; i++){
if(data[i].user == user){
userImgData.push(data[i]);
}else{
console.log('no data found!');
};
};
console.log(userImgData);
};
Here is how the data is coming across...
Edit: Changed userImgData to a state variable and got some results to show.
There appears to be a couple of issues present...
You're trying to loop over data before it is assigned a value. This is because the Axios request is asynchronous so the code after the request runs before the code in the then() callback.
userImgData isn't a state variable and even if it was, using push() won't trigger a state update and won't re-render your component.
Try using a memo hook to provide the filtered userImgData instead
const [data, setData] = useState([]);
const userImgData = useMemo(
() => data.filter((d) => d.user == user),
[data, user]
);
const getData = async () => {
setData((await axios.get("IP/imagefiles")).data);
};
You can check userImgData.length to conditionally render the list.

Have multiple GET requests and call state setter once they have all completed

I'm trying to render a list of favorite movies based on signed-in user's list.The flow is following:
I have ID of favorited movies in the firebase DTB for each user
User visits the "favorited" page and the collection of favorited movies is updated
Then the API call for movieDB is called for each movie to render movie list
Unfortunately I was able to update the array of objects only via push, which resulted in calling setContent(items) for every item which means that content variable exists even with 1st iteration, leaving the option to render the "trending" div as value is truth not rendering full content.
How can I either refactor the useEffect? Change the render conditions for "Trending" to be sure, that all values in content var are finished updating via API?
const Favorite = () => {
const { user } = useAuthContext();
const { documents: movies } = useCollection("favMovies", ["uid", "==", user.uid]); // only fetch movies for loged user
const [page, setPage] = useState(1);
const [content, setContent] = useState([]);
const [numOfPages, setNumOfPages] = useState();
useEffect(() => {
const items = [];
movies &&
movies.forEach(async (movie) => {
try {
const response = await axios.get(
`https://api.themoviedb.org/3/${movie.mediaType}/${movie.id}?api_key=${process.env.REACT_APP_API_KEY}&language=en-US`
);
items.push(response.data);
setContent(items);
} catch (error) {
console.error(error);
}
});
}, [movies]);
return (
<div>
<span className="pageTitle">Trending</span>
<div className="trending">
{content &&
content.map((con) => (
<SingleContent
key={con.id}
id={con.id}
poster={con.poster_path}
title={con.title || con.name}
date={con.first_air_date || con.release_date}
mediaType={con.media_type}
voteAverage={con.vote_average}
/>
))}
</div>
<CustomPagination setPage={setPage} />
</div>
);
};
export default Favorite;
You can overcome your problem by using Promise.all(). For that change your useEffect code to:
useEffect(() => {
const fectchMovies = async () => {
if (!movies) return;
try {
const promises = movies.map((movie) =>
axios.get(
`https://api.themoviedb.org/3/${movie.mediaType}/${movie.id}?api_key=${process.env.REACT_APP_API_KEY}&language=en-US`
)
);
const content = await Promise.all(promises);
setContent(content.map(c => c.data));
} catch (error) {
console.error(error);
}
};
fectchMovies();
}, [movies]);

How to make a JSON response data global :)

Currently working on a stock project for my portfolio and I am using finnhub as the API.
I can log everything to my console. However I cannot render it as the "data" is not globally declared and must be inside of a certain function.
I tried rendering globally but had no luck...
So my question is how do I make 'data' global so that I can render it inside of the "StockHeader's" return ?
Heres what I have so far...
import React,{ useState, useEffect } from 'react';
const StockHeader = (data) => {
const [stocks, setStocks] = useState({});
const getStocks = () => {
//setting stocks
setStocks(stocks)
}
//calling it once
useEffect(()=> {
getStocks();
}, [])
//using finhubs ready made code from documentation
const finnhub = require('finnhub');
const api_key = finnhub.ApiClient.instance.authentications['api_key'];
api_key.apiKey = "my apikey"
const finnhubClient = new finnhub.DefaultApi()
finnhubClient.quote("AAPL", (error, data, response) => {
//I can log the data but I cant show it in my component
console.log(data.c)
});
return (
<>
{/* This says that data is not defined */}
<h1>{data.c}</h1>
</>
)
}
export default StockHeader
You just need a little bit of code reorganization so that the API request only happens once and so that you can use setStocks to store it:
const StockHeader = (data) => {
const [stocks, setStocks] = useState({});
useEffect(()=> {
//this could be separated into a `getStocks` function if you want
const finnhub = require('finnhub');
const api_key = finnhub.ApiClient.instance.authentications['api_key'];
api_key.apiKey = "my apikey"
const finnhubClient = new finnhub.DefaultApi()
finnhubClient.quote("AAPL", (error, data, response) => {
console.log(data.c);
setStocks(data.c);
});
}, []);
return (
<>
{/* You probably don't want to render `stocks` itself, but this shows you how to get access to the variable */}
<h1>{stocks}</h1>
</>
)
}

How to render a React component that relies on API data?

I am trying to render a component within a component file that relies on data from an outside API. Basically, my return in my component uses a component that is awaiting data, but I get an error of dataRecords is undefined and thus cannot be mapped over.
Hopefully my code will explain this better:
// Component.js
export const History = () => {
const [dateRecords, setDateRecords] = useState(0)
const { data, loading } = useGetRecords() // A custom hook to get the data
useEffect(() => {
fetchData()
}, [loading, data])
const fetchData = async () => {
try {
let records = await data
setDateRecords(records)
} catch (err) {
console.error(err)
}
}
// Below: Render component to be used in the component return
const GameItem = ({ game }) => {
return <div>{game.name}</div>
}
// When I map over dateRecords, I get an error that it is undefined
const renderRecords = async (GameItem) => {
return await dateRecords.map((game, index) => (
<GameItem key={index} game={game} />
))
}
const GameTable = () => {
return <div>{renderRecords(<GameItem />)}</div>
}
return(
// Don't display anything until dateRecords is loaded
{dateRecords? (
// Only display <GameTable/> if the dateRecords is not empty
{dateRecords.length > 0 && <GameTable/>
)
)
}
If dateRecords is meant to be an array, initialize it to an array instead of a number:
const [dateRecords, setDateRecords] = useState([]);
In this case when the API operation is being performed, anything trying to iterate over dateRecords will simply iterate over an empty array, displaying nothing.
You've set the initial state of dateRecords to 0 which is a number and is not iterable. You should set the initial state to an empty array:
const [dateRecords, setDateRecords] = useState([]);

React Native Firebase fetch data

I'm trying to fetch data from firebase.
I want to push the values in an array and map through it in a view.
The only problem is that i can see the values in my array but not outside the .then function.
const dataArray = []
firebase
.database()
.ref("Challenges/Day")
.once("value")
.then((snapshot) => {
snapshot.forEach((child) => {
dataArray.push(child.val());
});
console.log(dataArray)
});
return (
<View style={styles.text}>
{dataArray.map((data) => {
<Text>{data}</Text>;
})}
</View>
);
if i console log it then the output is :
Array [
"eat",
"sleep",
]
But outside it's function my array is empty.
The issue here is console.log() is getting executed even before the data is returned from the firebase as response. When the request is made it takes sometime (say 1 sec) to return the response, but in the meantime the control executes the next line which is console.log(). When this line is executed, the value of dataArray=[] at that amoment. So for your case it is best to keep dataArray as state variable, so when the response is returned you can set the value of the dataArray which will re-render your component, thus displaying its return value.
const [dataArray,setDataArray] = useState([])
firebase
.database()
.ref("Challenges/Day")
.once("value")
.then((snapshot) => {
let tempArray=[];
snapshot.forEach((child) => {
tempArray.push(child.val());
}
setDataArray(tempArray);
);
console.log(dataArray)
});
return (
<View style={styles.text}>
{dataArray.map((data) => {
<Text>{data}</Text>;
})}
</View>
);
It's due to 2 issues you are facing.
asynchrounous - means what you are seeing in UI is the [] before firebase is executed.
state update - there were no state updated, hence even after the variable change occurred in firebase, the UI is not updated. Any normal variable changes will not re-render the UI, only a *state change will trigger a re-render in React.
What you can do.
const [dataArray, setDataArray] = useState([])
useEffect(() => {
firebase
.database()
.ref("Challenges/Day")
.once("value")
.then((snapshot) => {
snapshot.forEach((child) => {
//call an update to the array.
//setDataArray((oldDataArray) => oldDataArray.push(child.val()))
setDataArray((oldDataArray) => [...oldDataArray, child.val()])
});
console.log(dataArray)
});
}, []); //will only initialize once(on mount). Putting outside will get called again and again when state change.
return (
<View style={styles.text}>
{dataArray.map((data) => {
<Text>{data}</Text>;
})}
</View>
);

Categories