How to populate my const before the page renders? - reactjs

I'm pretty new to ReactJS dev and today i'm trying to get some data from an API using Axios. My issue is :
I'm trying to de map function on resultData to get what i want inside, but an error is proped and it's showing : resultData.map is not a function
If i comment this part of the code and just render the page first, then uncomment, it works and data are shown.
I'm assuming that data is not loaded before the rendering process is over so that's why i get this. But how to make it load before ?
Here my code snippets :
import React, { useState, useEffect } from "react";
import "./App.css";
import axios from "axios";
const Url = "someAPi";
function App() {
const [baseData, setBaseData] = useState({});
const [resultData, setResultData] = useState({});
useEffect(() => {
getBaseDataWithAxios();
}, []);
const getBaseDataWithAxios = async () => {
const response = await axios.get(Url);
setBaseData(response.data);
};
useEffect(() => {
getResultDataWithAxios();
}, []);
const getResultDataWithAxios = async () => {
const response = await axios.get(Url);
setResultData(response.data.result);
};
const listItems =
resultData.map((d) => <li key={d.value}>{d.value}</li>);
return (
<div className="App">
<header className="App-header">
<h2>generated fees</h2>
</header>
<div className="info-container">
<h5 className="info-item">{baseData.status}</h5>
<h5 className="info-item">{baseData.message}</h5>
<h5 className="info-item">{listItems[1]}</h5>
</div>
</div>
);
}
export default App;
The error is thrown on this :
const listItems =
resultData.map((d) => <li key={d.value}>{d.value}</li>);
I know my data can be read since if i comment the listItems and the displaying part in the return, render the page, uncomment everything, it displays the data properly.
Can someone explain to me how to populate data first ? During my research i've seen that this can happen by using Axios.
Thanks a lot !

The useEffect hook always runs after your component function returns in the render cycle.
Try an empty array for your initial value of resultData instead of an empty object:
const [resultData, setResultData] = useState([]);
There is no map built-in method on non-array objects, so during the first execution, you receive that error.

Related

Difficulties with useEffect and asyncawait

I've read several questions here regarding my current difficulty; they also told me the way I was coding it was wrong, and I changed it. However, even after changing I still can't seem to get the proper result.
I'm trying to make a small React HTTP Request app for learning purposes. According to the classes I've been following, I managed to create the json server, setup to watch for the DB.json properly, etc. Now, inside the App.js I'm trying to make the async\await call for listing the products in the console.
First, I had the following error:
"Effect callbacks are synchronous to prevent race conditions. Put the async function inside:"
I fixed it by changing my code. It was triggering a warning and I found out the classes I've been following are a bit outdate. No problem. However, even after changing it I can't view the products I create on db.json. If I go to localhost:3000/products it shows up there (which means things are working).
I believe I'm doing it the right way now, but I still can't seem to figure out why I can't view the data.
Any input is appreciated. Thanks!
ps: I'm just starting with react.
App.Js
import './App.css';
import { useState, useEffect } from "react";
const url="http:/localhost:3000/products";
function App() {
const [products, setProducts] = useState ([]);
useEffect(() => {
const fetchData = async () => {
const data = await fetch(url);
console.log("Data:" + data)
const res = await data.json();
console.log("Res:" + res)
setProducts(res);
}
fetchData();
}, []);
console.log(products);
return (
<div className="App">
<h1>LIsta de produtos</h1>
</div>
);
}
export default App;
The URL you put is missing a "/", Check if the URL is right, rest else seems to be correct, the code below should work.
import "./App.css";
import { useState, useEffect } from "react";
// URL is probably wrong, this is fixed URL
const url = "http://localhost:3000/products";
function App() {
const [products, setProducts] = useState([]);
useEffect(() => {
const fetchData = async () => {
const data = await fetch(url);
console.log("Data:" + data);
const res = await data.json();
console.log("Res:" + res);
setProducts(res);
};
fetchData();
}, []);
console.log(products);
return (
<div className="App">
<h1>LIsta de produtos</h1>
</div>
);
}
export default App;

How to show loading only after making a api request?

In my react Project,
There is a button. When clicking on it, it will start making an API request.
Right now, on the first page load, there is already a "Loading" showing, even before I click the right button.
Q1: How can I only show the "Loading" only after I set the click loading butting?
(For some reason, I am not able to use setLoading state to do this)
Q2:Even thought this example may seem so trivial, but taking the consideration that if the return is Error or resolved, there may be different handling, even thought I havent shown it in the example yet.
I have read some online doc, it said I may need to use useReducer for this. But I am not so sure how.
Notice: Much appreciate if could provide answer by using useReducer approach
Below is my code
import React , {useState, useEffect}from 'react';
import axios from 'axios';
export function App(props) {
const [post, setPost]= useState('')
useEffect(()=>{console.log(post)})
const handle = () => {
axios.get('https://jsonplaceholder.typicode.com/todos/1').then((response) => {
setPost(response.data);
});
}
return (
<div className='App'>
<button onClick={handle}>Load data</button>
{post? <ul>
<li>{post.userId}</li>
<li>{post.id}</li>
<li>{post.title}</li>
</ul>:<p>Loading</p>
}
</div>
);
}
=====================
Edited:
I have updated the code, but now I am stuck with the situation that when the loading is set to be true, the databoard is gone then
import React , {useState, useEffect}from 'react';
import axios from 'axios';
export function App(props) {
const [post, setPost]= useState('')
const [loading,setLoading]= useState(false);
useEffect(()=>{console.log(post)})
const handle = () => {
//before do the api call
//set the loading
setLoading(true);
axios.get('https://jsonplaceholder.typicode.com/todos/1').then((response) => {
setPost(response.data);
//set the loading to be false as loading is done
setLoading(false);
}).catch((err) => {
//error state
//set the loading to be false as loading is done
setLoading(false);
});
;
}
return (
<div className='App'>
<button onClick={handle}>Load data</button>
{loading? <Databoard post={post}>
</Databoard>:null
}
</div>
);
}
const Databoard = (props) => {
const {post}=props
return <ul>
<li>{post.userId}</li>
<li>{post.id}</li>
<li>{post.title}</li>
</ul>
}
You simply need a state variable using which you can judge what to show to the user.
const [loading,setLoading]= useState(false);
const handle = () => {
setLoading(true); axios.get('https://jsonplaceholder.typicode.com/todos/1').then((response) => {
setPost(response.data);
setLoading(false);
}).catch((err) => {
//error state
setLoading(false);
});
}
Use the variable in your return statement to handle the jsx

Deeply Nested API printing

I am working on a simple app that will print out images from an API. The API is pretty nested and I already made it into TS to view schema and looked at in Postman (nothing is wrong with the API this is an error on my part.) I know where the error is but I think I need some guidance here.
The component is literally just a simple js file that has the image integrated (from the map) so I did not think I needed to show it but I can if needed. Also, I did block out my API key but the error is in the json/API. I know the issue is between setState and mapping.
import React, {useEffect, useState, Component} from 'react';
import './App.css';
import Mars from './Mars.js'
const App = () => {
const App_Key = "bSfujNx0oXZ7T5czBchcMbfLMg7dYdC9YOR7ZqJZ"
const [mars, setMars] = useState([]);
useEffect (() => {
getMars();
}, []);
const getMars = async () => {
const response = await fetch(`https://api.nasa.gov/mars-photos/api/v1/rovers/curiosity/photos?sol=1000&camera=fhaz&api_key=xxxxxxxxxxxx
const data = await response.json();
console.log(data.photos);
setMars(data.data);
}
return(
<div className="App">
<h1>Mars Rover</h1>
{mars.map(mar => (
<Mars
image={mars.data.photos.data.img_src}
/>
))}
</div>
);
};
The response structure shows that the path of img_sc is data.photos[i].img_src. You are trying to access it using data.data.data.photos.data.img_src.
At the place where the state of mars is being set, use data.photos instead of data.data
setMars(data.photos);
And inside the map function
{mars.map(photo=> (
<Mars image={photo.img_src} />
))}

Why It Renders 'Undefined' first And Then Display The Object In The Log [duplicate]

This question already has an answer here:
Why does useState cause the component to render twice on each update?
(1 answer)
Closed 2 years ago.
How should I fix my React App which renders two logs with different results even though the code has only one console.log in the application? Yes, I removed <React.StrictMode> in my index.js file because it also trigger renders twice. In the terminal the first log is an undefined and the second one has the object with data, because of that when I use array map method, it keeps saying " BookDetail.jsx:26 Uncaught TypeError: bookData.map is not a function" .
I'm trying to fetch the detailed information about a book from the firebase database. My goal is very simple, a frontend user clicks a title of book and it takes to the detailed page. All the data stores in firestore database. The detailed page code is below, hoping somebody can help me out, thank you!
import React, {useState, useEffect} from 'react'
import {Link} from 'react-router-dom'
import firebase from '../config/fbConfig'
const BookDetails = (props) => {
const id = props.match.params.id
const db = firebase.firestore()
const [books, setBooks] = useState('')
useEffect(() => {
db.collection("books")
.doc(id)
.get()
.then(doc => doc.data())
.then(data => setBooks(data))
},[])
const bookData = {books}
return (
<div className="book_details">
<Link to="/"><h2>Home</h2></Link>
{console.log(bookData)}
<h1>The Summary Of the Book </h1>
{bookData.map(book => <div key={book.id}> {book.brief} </div>)}
</div>
)
}
export default BookDetails
Per ReactJS documentation, it's recommended to call hooks at top level of component: https://reactjs.org/docs/hooks-rules.html
So it looks like when your component mounts it calls your custom useBooks hook.
It initializes books state and then executes the useEffect. However, I think calling to the Firestore db function is an async process and because you're not waiting for the response, your function is returning undefined.
It seems like you may not need your custom hook. Set up useState and useEffect at the top level.
const BookDetails = (props) => {
const id = props.match.params.id
const db = firebase.firestore()
const [books, setBooks] = useState([])
useEffect(() => {
const fetchBooks = () => {
db.collection("books")
.doc(id)
.get()
.then(doc => doc.data())
.then(data => setBooks(data))
}
fetchBooks()
},[])
return (
<div className="book_details">
<Link to="/"><h2>Home</h2></Link>
{console.log(books.title)}
<h1>The Summary Of the Book </h1>
{books && books.map(book => <div key={book.id}> {book.brief} </div>)}
</div>
)
}
export default BookDetails
const data = doc.data() appears to be an async function, so you may want to chain another .then for setBooks

Cannot set data in variable after loading with useEffect()

So I am doing an online fullstack course for University of Helsinki and it is wanting me to use a weather and climate API to show information about the country and its current weather.
So I have three components: App, Lister, and Country. Inside App I make a useEffect call with axios to get the data for a list of countries. From there I want it so that when you click on the button or search it should show information about the country, including weather. To do that, when information about the specific country is shown, I have another useEffect call to an API for the weather. However, while the first API call seems to store the data on the list of countries, the second call does not.
I have tried changing the dependency of the second useEffect call to [name], but that also does not bring results, and when I just log the information with that type of dependency, I just see an infinite amount of calls in the console. Have tried with other API's just to see if I was miscalling it somehow but even those resulted in the same situation.
Inside the App component I do a call to get info about all the countries and it works fine.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Lister from './Lister';
const App = () => {
const [places, setPlaces] = useState([]);
const [search, setSearch] = useState('');
const [results, setResults] = useState('');
useEffect(() => {
axios.get('https://restcountries.eu/rest/v2/all').then(r => setPlaces(r.data));
}, []);
.....
Everything works fine in here. I'm able to see and search and all that. I then have it call Lister which just maps the data from the country-search API into using the Country component.
.....
const listPlaces = () => {
if (results.length === 1) {
return (
<div>
{results.map(r => (
<Country
key={r.population / r.name.length + 5}
name={r.name}
capital={r.capital}
population={r.population}
languages={r.languages}
image={r.flag}
/>
))}
</div>
);
}
This is where the main crux of the issue is, in Country. When I call useeffect again, this time for the weather api, it doesn't seem to set it to the weather variable
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const Country = ({ name, capital, population, languages, image }) => {
const [weather, setWeather] = useState([]);
// have tried useState(), useState([]), useState({})
const accessKey = '';
useEffect(() => {
axios
.get(`http://api.weatherstack.com/...`)
.then(r => {
setWeather(r.data);
console.log(r.data);
});
}, []);
return (
<div>
<div className='Country'>
<h1>Name: {name}</h1>
<p>Capital: {capital}</p>
<p>Population: {population}</p>
<h2>Languages</h2>
<ul>
{languages.map(l => (
<li key={l.name.length}>{l.name}</li>
))}
</ul>
<img src={image} alt="Country's flag" height='100' width='100' />
</div>
<div className='Weather'>
<h1>Weather {weather.current.temperature} </h1>
<h2>Temperature </h2>
</div>
</div>
);
};
export default Country;
Weather will be of type undefined even though the log shows that the data was loaded, so should it not be set by the setWeather call?

Resources