Unable to access nested object from JSON data - javascript

I have the following function component in React:
function GetData() {
const [randomDataJSON, setRandomDataJSON] = useState('');
const url = 'https://randomuser.me/api/';
const getData = () => {
axios
.get(`${url}`)
.then((results) => {
const userData = results.data.results;
setRandomDataJSON(JSON.stringify(userData, null, 2));
})
.catch((err) => console.error(err));
};
return (
<div>
<h3>GetData Component</h3>
<pre>{randomDataJSON[0].name.first}</pre>
<button onClick={getData}>Get Data</button>
</div>
);
}
export default GetData;
The JSON data from the API is as follow:
[
{
"gender": "female",
"name": {
"title": "Miss",
"first": "Barbara",
"last": "Sullivan"
},
...
I would like to access and display the first name of the JSON data from the API by using {randomDataJSON[0].name.first in the <pre> tag. However, I keep getting the following error message: TypeError: Cannot read properties of undefined (reading 'name')

You are setting json string to randomDataJSON state variable, and trying use JSON string as an object. You can try to console.log(randomDataJSON) to confirm my suspicions.
I think you should not convert your data object to json in first place, so setRandomDataJSON(JSON.stringify(userData, null, 2)); will be setRandomDataJSON(userData);
function GetData() {
const [randomData, setRandomData] = useState('');
const url = 'https://randomuser.me/api/';
const getData = () => {
axios
.get(`${url}`)
.then((results) => {
const userData = results.data.results;
setRandomData(userData, null, 2);
})
.catch((err) => console.error(err));
};
return (
<div>
<h3>GetData Component</h3>
<pre>{randomData[0].name.first}</pre>
<button onClick={getData}>Get Data</button>
</div>
);
}
export default GetData;

At the time the page loads axios wouldn't have ran to request the array so randomDataJSON is a string at that time. You could do
const [randomDataJSON, setRandomDataJSON] = useState([]);
Above you set it to an empty array then check if it has length
<pre>{randomDataJSON.length > 0 && randomDataJSON[0].name.first}</pre>

Thank you very much to everyone pointing me in the right direction. My new code is as follow. My problem was I didn't know React doesn't allow you to render Javascript Object. To fix this, I just use the map() method to map through the data and display the properties in the object.
function GetData() {
const [randomDataJSON, setRandomDataJSON] = useState([]);
const url = 'https://randomuser.me/api/';
const getData = () => {
axios
.get(`${url}`)
.then((results) => {
const userData = results.data.results;
setRandomDataJSON(userData);
})
.catch((err) => console.error(err));
};
console.log('It is an', typeof randomDataJSON);
console.log('randomDataJSON is ', randomDataJSON);
return (
<div>
<h3>GetData Component</h3>
{randomDataJSON.map((data) => {
return <p>{data.name.first}</p>;
})}
<button onClick={getData}>Get Data</button>
</div>
);
}
export default GetData;

Related

How to replace 'useResult' in graphql with vue and apolo?

I have to replace this useResult that is fetching data from graphql
const locationOptions = useResult(
result,
[],
({ getLocations }): Option[] => formatOptions(getLocations)
)
and I want to change it for a computed function like
const locationOptions = computed(() => result.value.getLocations ?? [])
I was trying to use a watch to run the function but it seems not to be working
watch(locationOptions, () => {
formatOptions(locationOptions.value)
})
any suggestions?
You can use the format function already in the computed function:
const locationOptions = computed(() => {
return result.value?.getLocations ? formatOptions(result.value.getLocations) || []
})

Put results of a Get request from axios to an array

I don't understand a very simple task, I made a request from a API with axios in react.
If I console log the res.data, is like 170 result of single objects on my console.
I need to convert all these result in a single array of objects.
It's a basic task but I don't understand how to do it.
The application is a Trello Clone.
I have a variable called board that has all the data and with this list request, I grab all the column the the trello and append to ListObjects [] in newBoardData (it's a clone of board)
Here is my code:
//Get Request
const getList = async (id) => {
try {
return await axios.get(`${ENDPOINT}/lists/${id}`);
} catch (err) {
console.log(err);
}
};
const [loading, setLoading] = useState(false);
// Use Effect for grab the data with the listId
useEffect(() => {
(async () => {
setLoading(true);
const res = await (getList(listId));
//Loading up the listObjects
const oldList = board.board.listObjects
const newList = []
const payload = res.data;
//Adding all the old values to the new list (except for the current payload id)
for(let obj of oldList){
if(obj._id !== payload._id) newList.push(obj)
}
//Adding the current payload id
newList.push(payload)
const data = {
...board,
board: {...board.board, listObjects: newList}
};
setList(res.data);
// Here I put the data objects with the new ListObjects Array
setBoardNew(data);
setLoading(false);
})();
}, []);
Here is the console log of the get request res.data:
console.log of res.data
here is the board object:
board object
You can saw that there is a spam of result with the current res.data in ListObjects
I'think it make a request for every card in every list.
thank you very much!
UPDATE:
I will explain how the app works:
I have a file called Board.js, where I make this call (in the console log I have two call if I have two columns):
try {
return await axios.get(`${ENDPOINT}/boards/${id}`);
} catch (err) {
console.log(err);
}
};
useEffect(() => {
(async () => {
setLoading(true);
const res = await (getUserBoard(match.params.id));
if (res) {
axios.defaults.headers.common['boardId'] = match.params.id;
} else {
delete axios.defaults.headers.common['boardId'];
}
const payload = { ...res.data, listObjects: [], cardObjects: [] };
const data = {
...state,
board: { ...state.board, ...payload },
};
setBoardData(data);
setLoading(false);
})();
}, []);
Then I send the props data to the file List.js
{board.board.lists.map((listId, index) => (
<List key={listId} listId={listId} index={index} board={board} />
The list file send the data to
card.js
{list.cards.map((cardId, index) => (
<Card key={cardId} cardId={cardId} list={list} index={index} board={boardNew} />
The logic is: There is the board(board.js), in the board there are the lists (column)(list.js) in the lists there are the cards (card.js)
I hope it's more clear.
simple use this approach to add your new id value into the array state.
this.setState({ myArray: [...this.state.myArray, 'new value'] }) //simple value
this.setState({ myArray: [...this.state.myArray, ...[1,2,3] ] }) //another array

React.js fetch multiple endpoints of API

I am doing a React.js project. I am trying to pull data from an API that has multiple endpoints. I am having issues with creating a function that pulls all the data at once without having to do every endpoint separetly. The console.log gives an empty array and nothing gets display. The props 'films' is data from the parent and works fine. It is also from another enpoint of the same API. This is the code:
import { useEffect, useState } from "react";
import styles from './MovieDetail.module.css';
const MovieDetail = ({films}) => {
const [results, setResults] = useState([]);
const fetchApis = async () => {
const peopleApiCall = await fetch('https://www.swapi.tech/api/people/');
const planetsApiCall = await fetch('https://www.swapi.tech/api/planets/');
const starshipsApiCall = await fetch('https://www.swapi.tech/api/starships/');
const vehicleApiCall = await fetch('https://www.swapi.tech/api/vehicles/');
const speciesApiCall = await fetch('https://www.swapi.tech/api/species/');
const json = await [peopleApiCall, planetsApiCall, starshipsApiCall, vehicleApiCall, speciesApiCall].json();
setResults(json.results)
}
useEffect(() => {
fetchApis();
}, [])
console.log('results of fetchApis', results)
return (
<div className={styles.card}>
<div className={styles.container}>
<h1>{films.properties.title}</h1>
<h2>{results.people.name}</h2>
<p>{results.planets.name}</p>
</div>
</div>
);
}
export default MovieDetail;
UPDATE
I just added the post of Phil to the code and I uploaded to a codesanbox
You want to fetch and then retrieve the JSON stream from each request.
Something like this
const urls = {
people: "https://www.swapi.tech/api/people/",
planets: "https://www.swapi.tech/api/planets/",
starships: "https://www.swapi.tech/api/starships/",
vehicles: "https://www.swapi.tech/api/vehicles/",
species: "https://www.swapi.tech/api/species/"
}
// ...
const [results, setResults] = useState({});
const fetchApis = async () => {
try {
const responses = await Promise.all(Object.entries(urls).map(async ([ key, url ]) => {
const res = await fetch(url)
return [ key, (await res.json()).results ]
}))
return Object.fromEntries(responses)
} catch (err) {
console.error(err)
}
}
useEffect(() => {
fetchApis().then(setResults)
}, [])
Each URL will resolve to an array like...
[ "people", [{ uid: ... }] ]
Once all these resolve, they will become an object (via Object.fromEntries()) like
{
people: [{uid: ... }],
planets: [ ... ],
// ...
}
Take note that each property is an array so you'd need something like
<h2>{results.people[0].name}</h2>
or a loop.

JavaScript Array.filter() and .map() don't work, uncaught TypeError for Search function

So I'm trying to implement a search bar in my Flask application that will list out the cities that are being inputted by the user and exist in the JSON API results of a weather API.
I am following a tutorial and basically have the same code as ths: https://codepen.io/jamesqquick/pen/XWJxBQv
However, in my implementation, the .filter() and .map() functions don't work, I get the following error:
TypeError for map() and filter()
How do I fix this?
Here's my code (the regular generateHTML in the first part of the code with fetching current weather data already works, only the "SEARCH BAR" section has problems):
let currentType = "current.json";
let userCity = "London";
const apiData = {
url: "http://api.weatherapi.com/v1",
type: `${currentType}`,
key: "40cd513af8aa446484a92837213011",
city: `${userCity}`,
};
const { url, type, key, city } = apiData;
const apiUrl = `${url}/${type}?key=${key}&q=${city}`;
console.log("apiUrl:");
console.log(apiUrl);
fetch(apiUrl)
.then((data) => {
if (data.ok) {
return data.json();
}
throw new Error("Response not ok.");
})
.then((locationRequest) => generateHtml(locationRequest))
.catch((error) => console.error("Error:", error));
const generateHtml = (data) => {
console.log("data:")
console.log(data);
console.log("data.location.name:")
console.log(`${data.location.name}`);
const html = `
<div class="weather-location">
<h1>${data.location.name}, ${data.location.country}</h1></div>
<div class="details">
<span>Tmp: ${data.current.temp_c} °C</span>
<span>Feels like: ${data.current.feelslike_c} °C</span>
</div>
`;
const weatherDiv = document.querySelector(".weather");
weatherDiv.innerHTML = html;
};
/* SEARCH BAR */
const citiesList = document.getElementById('weather-cities');
const searchBar = document.getElementById('weather-searchbar');
let cities = [];
console.log("citiesList:");
console.log(citiesList);
console.log("searchBar:");
console.log(searchBar);
searchBar.addEventListener('keyup', (e) => {
userCity = e.target.value.toLowerCase();
console.log("usercity:");
console.log(userCity);
const filteredCities = cities.filter((city) => {
return (
city.name.toLowerCase().includes(userCity) ||
city.region.toLowerCase().includes(userCity) ||
city.country.toLowerCase().includes(userCity)
);
});
displayCities(filteredCities);
});
const loadCities = async () => {
try {
currentType = "search.json";
const res = await fetch(apiUrl);
cities = await res.json();
console.log("cities:");
console.log(cities);
displayCities(cities);
} catch (err) {
console.error(err);
}
};
const displayCities = (cities) => {
let htmlString = cities
.map((city) => {
return `
<li class="character">
<h2>${city.location.name}</h2>
<p>Temperature: ${city.current.temp_c} °C</p>
<p>Feels like:${city.current.feelslike_c} °C></p>
</li>
`;
})
.join('');
citiesList.innerHTML = htmlString;
};
loadCities();
<div class="other-stats">
<div class="weather-search">
<input type="text" id="weather-searchbar" placeholder="Your city"></input>
<ul id="weather-cities"></ul>
</div>
<div class="weather"></div>
</div>
<script src="../static/weather_api.js"></script>
Array.prototype.filter() and Array.prototype.map() are used for arrays, the cities property is getting a javascript object. You need to assign an array to "cities".
Ok, it seems I solved the issue for now. In displayCities, for the HTML section I put city.location.name like I'd do to get the name in "current.json" API call, but in the new API call "search.json" I get an array of dictionaries that contain different information directly, not with different categories like "location" or "current". So city.name is enough. To clarify better, see console.log entries:
API call "current.json"
API call "search.json"
const displayCities = (cities) => {
let htmlString = cities
.map((city) => {
return `
<li class="character">
<p>${city.name}</p>
</li>
`;
})
.join('');
citiesList.innerHTML = htmlString;
};

.map is returning only the last element of array React

I'm creating a blog type app with cloud firestore just as a project to learn some stuff. However I'm having an issue where only the last element of my state array, 'blogs', is being returned inside of the .map method.
Any help with this is appreciated, thanks!
const [blogs, setBlog] = useState([]);
const fetchBlogs = async () => {
const response = await getDocs(collection(db, "blog posts"));
let postArray = [];
response.docs.forEach((blogpost) => {
postArray.push(blogpost.data());
});
console.log(postArray)
postArray.forEach(post => {
setBlog([...blogs, post]);
});
}
useEffect(() => {
fetchBlogs();
}, []);
return (
<ul className="posts-container">
{ blogs.map((blog) => {
console.log(blog); //this outputs each of the individual posts, but only the last is displayed as an <li>
return ( <li key={blog.title}>{blog.title}</li> )
})}
</ul>
)
You probably want to put the response in your state without doing the forEach()
setBlog(postArray);
Or even (depending on data structure)
const [blogs, setBlog] = useState([]);
const fetchBlogs = async () => {
const response = await getDocs(collection(db, "blog posts"));
setBlog(response.data); // set to whatever you need
}
useEffect(() => {
fetchBlogs();
}, []);
return (
<ul className="posts-container">
{ blogs.map((blog) => {
return <li>( <li key={blog.title}>{blog.title}</li> )
}
)}
</ul>
);
Instead of this:
postArray.forEach(post => {
setBlog([...blogs, post]);
});
Try doing this:
setBlog(postArray);
I am not sure as to what exactly happens, but this seems like the most suspicious piece in your code.
You are overriding the blog post twice, try it this way
const fetchBlogs = async () => {
const response = await getDocs(collection(db, "blog posts"));
setBlog([...blogs, ...response.docs.map(blogpost => blogpost.data())])
}
useEffect(() => fetchBlogs(), []);

Categories

Resources