SWAPI request in React - reactjs

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.

Related

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

Why is my React Use-Effect Hook Not Working?

I am re-posting a question I asked a week ago where I left out the steps I took to solve the issue. I am working on a simple MERN app off a tutorial and the use-effect function is not rendering the content onto the page. Here is the code:
App.js File
import './App.css';
import { useState, useEffect } from 'react';
import Axios from 'axios';
function App() {
const [listOfUsers, setListOfUsers] = useState([]);
useEffect(() => {
Axios.get("http://localhost:3001/getUsersFakeDataGen").then((response) => {
setListOfUsers(response.data)
})
}, [])
return (
<div className="App">
<div className="usersDisplay">
{listOfUsers.map((user) => {
return (
<div>
<h1>Name: {user.name}</h1>
<h1>Age: {user.age}</h1>
<h1>Username: {user.username}</h1>
</div>
)
})}
</div>
</div>
)
};
export default App;
I tested the functionality by commenting out the "useEffect()" function and putting in an object in the "useState([])" element of "function App()". That object did correctly render on the page, but when I deleted that object and un-commented useEffect(), the page was blank again.
I confirmed that my APIs are working because my API client (Thunder Client) is showing that the GET and POST requests are reading and writing to the database (MongoDB). Also, the server is working properly (confirmed by a console log).
Any suggestions would be appreciated. If more information is needed, please let me know. Thank you.
if your problem is not resolved, yet I suggest the following:
import axios from 'axios'
...
const [listOfUsers, setListOfUsers] = useState([]);
const fetchData = async () => {
const result = await axios.get("http://localhost:3001/getUsersFakeDataGen").then((response) => {
setListOfUsers(response.data)
return response.data;
});
useEffect(() => {
fetchData();
}, [])
Note [] in the useEffect, it means it will render only once when the page loads. Also I used async and await to wait for data to be retrieved before processing (maybe that's why you get empty elements). You can also setState outside of useEffect.
import './App.css';
import { useState, useEffect } from 'react';
import Axios from 'axios';
function App() {
const [listOfUsers, setListOfUsers] = useState([]);
useEffect(() => {
Axios.get("http://localhost:3001/getUsers").then((response) => {
setListOfUsers(response.data)
})
}, [listOfUsers]); // THIS IS WHERE YOU ADD YOUR useEffect DEPENDENCIES
return (
<div className="App">
<div className="usersDisplay">
{listOfUsers.map((user) => {
return (
<div>
<h1>Name: {user.name}</h1>
<h1>Age: {user.age}</h1>
<h1>Username: {user.username}</h1>
</div>
)
})}
</div>
</div>
)
};
export default App;
OK look so the issue is if you only provided an empty array as your second argument. Your useEffect will only run one time, when you add stateful values to the array the useEffect will render every time that piece of state changes. If you omit the second argument the useeffect will run over and over again.
Also here-- Remember that you array starts empty, You need a check it's existence
{listOfUsers?.map.map((item))=>{}}
or
{listOfUsers.length && listOfUsers.map((item))=>{}}
.
{listOfUsers.map((user) => {
return (
<div>
<h1>Name: {user.name}</h1>
<h1>Age: {user.age}</h1>
<h1>Username: {user.username}</h1>
</div>
)
})}

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.

React hooks async problems: map is executing before data is returned - solutions?

In the code below, you can see that I'm mapping over data returned from an axios GET request. I'm then passing this data through a filter, and setting it to state (under the variable gig).
I'm then wanting to map over the data held in gig - only problem is that when I try, I get an error say that TypeError: gig.map is not a function, and gig console logs to undefined.
However, when gig is console logged inside the useEffect method, it returns the data I want.
So I'm guessing that what is happening is that setState is aysnc, and the gig.map function is being reached before the gig has been set to filteredGigs.
Any suggestions on how to fix this?
Here's the full code:
import React, {useState, useEffect} from 'react'
import axios from 'axios'
import { auth } from 'firebase/app'
const UniqueVenueListing = (props) => {
const [gig, setGig] = useState([])
const authUserId = props.userDetails.uid
useEffect(()=>{
axios.get("https://us-central1-gig-fort.cloudfunctions.net/api/getGigListings")
.then(res => {
let filteredGigs = res.data
.filter(gig => {
return gig.user !== authUserId
})
setGig({gig: filteredGigs})
console.log(gig)
})
},[])
useEffect(() => {
console.log(gig)
}, [gig])
return(
<div>
{
gig.map(gigs => {
return gigs.user
})
}
</div>
)
}
export default UniqueVenueListing
Issue
You change the state shape. Initial shape of gig state is an empty array([]), but in the effect you store an object with an array under key gig ({ gig: filteredGigs }). Additionally since state updates are asynchronous, the console.log after setGig will only log the current state, not the one just enqueued.
Solution
Just save the filtered gig array into state. This will keep the gig state an array and later in the return gig.map(... will work as expected.
useEffect(()=>{
axios.get("https://us-central1-gig-fort.cloudfunctions.net/api/getGigListings")
.then(res => {
const filteredGigs = res.data.filter(gig => {
return gig.user !== authUserId
})
setGig(filteredGigs); // <-- store the array in state
})
},[])

Can't render data from API being passed down as props (ReactJS)

I'm really stuck in trying to render some data being passed down as props. I'll include some code and definitions below, but if you feel that I need to include some further code snippets, please let me know (I'm really struggling to find what's causing the error, so I may have missed out the causal issue!).
I first take data from an API which is then used to populate a UserList component via useState (setUsers(data):
useEffect(() => {
async function getUserList() {
setLoading(true);
try {
const url =
"API URL";
const response = await fetch(url);
const data = await response.json();
setUsers(data);
} catch (error) {
throw new Error("User list unavailable");
}
setLoading(false);
}
getUserList();
}, []);
If a user is clicked in the UserList, this changes the selectedUser state of the parent Home component to be the specific user's unique_ID via:
onClick={() => setSelectedUser(unique_ID)}
If the selectedUser changes, the Home component also does a more updated data fetch from the API to get all information relevant to the specific user via their unique_ID:
useEffect(() => {
async function getSelectedUserData() {
try {
const url = `API URL/${selectedUser}`;
const response = await fetch(url);
const data = await response.json();
setSelectedUserData(data);
} catch (error) {
throw new Error("User data unavailable");
}
}
getSelectedUserData();
}, [selectedUser]);
The specific user data is then passed down as props to a child UserInformation component:
<UserInformation selectedUser={selectedUser} selectedUserData={selectedUserData} />
At this point, I can see all the data being passed around correctly in the browser React Developer Tools.
The UserInformation component then gets the data passed via props:
import React, { useEffect, useState } from "react";
function UserInformation({ selectedUser, selectedUserData }) {
const [currentUser, setCurrentUser] = useState({ selectedUserData });
useEffect(() => {
setCurrentUser({ selectedUserData });
}, [selectedUser, selectedUserData]);
return (
<div>
<p>{selectedUserData.User_Firstname}</p>
<p>{currentUser.User_Firstname}</p>
</div>
);
}
export default UserInformation;
And here is where I get stuck - I can't seem to render any of the data I pass down as props to the UserInformation component, even though I've tried a few different methods (hence the <p>{selectedUserData.User_Firstname}</p> and <p>{currentUser.User_Firstname}</p> to demonstrate).
I'd really appreciate any help you can give me with this - I must be making an error somewhere!
Thanks so much, and sorry for the super long post!
I managed to solve this (thanks to the help of Mohamed and Antonio above, as well as the reactiflux community).
import React from "react";
function UserInformation({ selectedUserData }) {
const currentUserRender = selectedUserData.map(
({ User_Firstname, User_Lastname }) => (
<div key={unique_ID}>
<p>{User_Firstname}</p>
</div>
)
);
return (
<div>
{selectedUserData ? currentUserRender : null}
</div>
);
}
export default UserInformation;
As selectedUserData was returning an array instead of an object, I needed to map the data rather than call it with an object method such as {selectedUserData.User_Firstname}.
const currentUserRender = selectedUserData.map(
({ User_Firstname, User_Lastname }) => (
<div key={unique_ID}>
<p>{User_Firstname}</p>
</div>
)
);
The above snippet maps the selected data properties found inside selectedUserData ({ User_Firstname, User_Lastname }), with the whole map being called in the return via {selectedUserData ? currentUserRender : null}.
Hopefully my explanation of the above solution is clear for anyone reading, and a big thanks again to Mohamed and Antonio (as well as a few others in the reactiflux Discord community) for helping!
You're trying to set the current user to an object with key "selectedUserData".
So if you want to access it you've to access it by this key name so change this line currentUser.User_Firstname to currentUser.selectedUserData.User_Firstname

Resources