Deeply Nested API printing - reactjs

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} />
))}

Related

How do I make Next.js 13 server-side components in the app directory that depend on useEffect for props?

I'm trying to write a Next.js 13 newsletter page in the app directory that uses server-side components that depend on useEffect for props. The useEffect fetches data from a REST API to get newsletters which will render the content of the page. The code I'm using is below. I'm having trouble figuring out how to configure the server-side components to work when I need to "use client" for interactivity. How can I make sure that the server-side components are rendered before it is sent to the client?
Code:
import Navbar from '#/components/navbar'
import Footer from '#/components/footer'
import Pagination from './pagination'
import IssueCards from './issueCards';
import { useState, useEffect } from 'react';
import axios from 'axios';
const Newsletters = () => {
const [issues, setIssues] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [issuesPerPage, setIssuesPerPage] = useState(5);
useEffect(() => {
const fetchIssue = async () => {
const res = await axios.get(`${process.env.NEXT_PUBLIC_BACKEND_API}/newsletters`)
setIssues(res.data)
}
fetchIssue()
}, [])
// Change page
const paginate = (pageNumber) => setCurrentPage(pageNumber);
const indexOfLastIssue = currentPage * issuesPerPage;
const indexOfFirstIssue = indexOfLastIssue - issuesPerPage;
const currentIssues = issues.slice(indexOfFirstIssue, indexOfLastIssue)
return (
<>
<Navbar />
<div className="newsletter-container" id='newsletter-container'>
<h1>Newsletters</h1>
<hr></hr>
<div className="newsletter-wrapper">
<IssueCards issues={currentIssues} />
<Pagination
issuesPerPage={issuesPerPage}
totalIssues={issues.length}
paginate={paginate}
/>
</div>
</div>
<Footer />
</>
);
}
export default Newsletters;
How do I configure Next.js 13 server-side components that depend on useEffect for props and ensure that the content is rendered before it is sent to the client?
I tried following the Nextjs docs on Server and Client components but I am unsure of how I can pass down the props information onto the server.
Unfortunately, server components don't allow for hooks such as useEffect, see documentation here.
You have two main options:
New way of fetching data
Server components allow for a new way of fetching data in a component, described here.
This approach would look something this:
async function getData() {
const res = await fetch('https://api.example.com/...');
// The return value is *not* serialized
// You can return Date, Map, Set, etc.
// Recommendation: handle errors
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error('Failed to fetch data');
}
return res.json();
}
export default async function Page() {
const data = await getData();
return <main></main>;
}
Revert to client components
Your other option is to use the use client directive at the top of your file and leaving Newsletter as a client component. Of course, this way, you wouldn't get the benefits of server components, but this would prevent you from having to change your code substantially. Also, keep in mind that server components are still in beta.

How to populate my const before the page renders?

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.

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

SWAPI request in React

I am trying to get SWAPI data from 'people' using react. I would ultimately like to retrieve the data and then set the people and create a card from the 10 people on page 1. When I console.log my response I am able to see the object returned. I am trying to set that using response.data.results (should contain people).
//full code:
import React, { useState, useEffect } from 'react';
import axios from "axios";
import Cards from "./components/Card"
function People() {
const [people, setPeople] = useState([]);
useEffect(() => {
axios.get('https://swapi.co/api/people/')
.then(res => {
//console.log(res);
setPeople(res.data.results)
})
.catch(err => {
console.log(`Data not received ${err}`)
})
}, [])
return (
<div className = "container">
{people.map((name, index) => {
return <Cards name={name} index={index}/>
})}
</div>
)
}
export default People;
When I console.log swPeople after using setswPeople I am returned an empty array.
Any ideas as to why the set is not giving me an array containing the 10 people on page one?
I see it working https://codesandbox.io/s/react-hooks-useeffect-frhmn
it take time to set the state , if we dont pass the second argument [] to useEffect you will see it is returning data correctly but that will cause the infinite loop , so we avoid that
import React, { useState, useEffect } from 'react';
import axios from "axios";
import Cards from "./components/Card"
function People() {
const [people, setPeople] = useState([]);
useEffect(() => {
axios.get('https://swapi.co/api/people/')
.then(res => {
//console.log(res);
setPeople(res.data.results)
})
.catch(err => {
console.log(`Data not received ${err}`)
})
}, [])
return (
<div className = "container">
{people.map((name, index) => {
return <Cards name={name} index={index}/>
})}
</div>
)
}
looks like this worked after all but it was taking close to 30s for me to see that info logged in console and I was being impatient
Have you tried to enter this url in your browser, https://swapi.co/api/people?
Because it seems the link is redirecting to another url while it needs to brign you back a JSON.
If you want to use SWAPI info replace you SWAPI to https://www.swapi.tech/api/people
it works well.
However I suggeust you to download the extension of JSONVue it will help you track your data with comfortable JSON view in your broweser.
And about the info of the 10 people you trying to get from SWAPI, when you'll open the browser with the new SWAPI adress, try to track the path you want to do in your code. You'll see the info you're trying to catch is leading to another API.

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