I have the following function component
import React ,{useState,useEffect} from "react";
export default function Statistics() {
const [data, setData] = useState([]);
useEffect(() => {
fetch("https://hn.algolia.com/api/v1/search")
.then(response => response.json())
.then(data => setData(data));
}, []);
return (
data.map(el =>
<div>{el.title}</div>
)
);
}
I get the error:
Uncaught TypeError: data.map is not a function
what is the problem in the previous code ?
Just checked the API endpoint what you are working with. It seems you need the hits property because that's an array. In your current solution data is an object which does not have .map() to call. If you modify from data to data.hits then you can iterate through the array with .map().
Try the following:
useEffect(() => {
fetch("https://hn.algolia.com/api/v1/search")
.then(response => response.json())
.then(data => setData(data.hits));
}, []);
I hope this helps!
Related
I understand that useEffect() hook is supposed to fire on the first render. In my code, I am getting errors for not being able to use my States properties. This is because the State doesn't have the API data before the DOM renders. I guess I am just not understanding if I am missing something, but the console log shows that the questions array is empty twice before actually getting any data. If I am not trying to access the data anywhere, no data is thrown, but as soon as I try to use it, the code says that the data doesn't exist. I tried to use async but get the same error.
export default function Quiz(props) {
const [questions, setQuestions] = React.useState([])
React.useEffect(() => {
fetch("https://opentdb.com/api.php?amount=10")
.then(res => res.json())
.then(data => setQuestions(data.results))
}, [])
console.log(questions)
return (
<div>
<div className="quiz--question">
<h1>{questions.category}</h1>
</div>
</div>
)
}
questions is an array , so you need to iterate that to display the value. You can use map
import * as React from 'react';
import './style.css';
export default function App() {
const [questions, setQuestions] = React.useState([]);
React.useEffect(() => {
fetch('https://opentdb.com/api.php?amount=10')
.then((res) => res.json())
.then((data) => {
setQuestions(data.results);
console.log(questions);
});
}, []);
return (
<div>
{questions.map((elem) => (
<div className="quiz--question">
<h1>{elem.category}</h1>
</div>
))}
</div>
);
}
Demo Here
I'm using here useState to store data, update current value, by using this it's not store previous data, it's only store update current value which do useState. But I want to store previous data and update data all of them, somethings like, i have 10 api's data and i want to call them end of my api using ids.
i want to store 10 api's data in one array.
how to store 10 apis data in together using useState();
my code is somethings like:
const [dataLoc, setDataLoc] = useState([]);
const ids = [1,2,3,4,5,6,7,8,9,10]
useEffect(() => {
ids?.map((id) => {
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => response.json())
.then((dataLoc) => setDataLoc(dataLoc.title))
.catch((error) => console.error(error))
})
}, []);
console.log(dataLoc);
output is:
enter image description here
but it's in 10 array the out put but i want all the title in one array.
anyone can help me how can i do this here?
ThankYou for Your Trying in advance!
You can make use of react states prev-callback like that:
const [dataLoc, setDataLoc] = useState([]);
useEffect(() => {
ids?.map((id) => {
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => response.json())
.then((dataLoc) => setDataLoc((prev) => [...prev, dataLoc.title]))
.catch((error) => console.error(error));
});
}, []);
console.log(dataLoc);
By spreading ...prev and appending it with , dataLoc.title you add each entry to the previous state value. The order is the one in which the calls go through, since we insert the new element at the end.
Edit
If you want to console.log multiple times you can make use of another useEffect inside your component:
useEffect(() => {
console.log(dataLoc);
}, [dataLoc]);
Issue
The code is enqueueing state updates in a loop and not updating from the previous state. In other words, each enqueued update in the loop overwrites the previous update. The update from the last loop iteration is the one the app sees on the subsequent render.
Solution
Either use a functional state update to correctly enqueue state updates and update from the previous state. Use .forEach instead of .map.
const [dataLoc, setDataLoc] = useState([]);
useEffect(() => {
ids?.forEach((id) => {
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => response.json())
.then((dataLoc) => setDataLoc(dataLoc => [...dataLoc, dataLoc.title]))
.catch((error) => console.error(error))
})
}, []);
Or map the ids to an array of Promises and Promise.all them and enqueue a single state update.
const [dataLoc, setDataLoc] = useState([]);
useEffect(() => {
const requests = ids?.map((id) => {
return fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => response.json())
.then((dataLoc) => return dataLoc.title)
.catch((error) => console.error(error))
});
Promise.all(requests)
.then(ids => {
setDataLoc(ids)
});
}, []);
In your case it is overriding the previous values. You can merge the values
React.useEffect(() => {
ids?.map((id) => {
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => response.json())
.then((dataLoc) =>
setDataLoc((prevState) => {
return [...prevState, dataLoc.title];
})
)
.catch((error) => console.error(error));
});
}, []);
you can use push method to add each title to the Array:
const [dataLoc, setDataLoc] = useState([]);
const ids = [1,2,3,4,5,6,7,8,9,10]
useEffect(() => {
ids?.map((id) => {
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => response.json())
.then((dataLoc) => setDataLoc(dataLoc.push(dataLoc.title)))
.catch((error) => console.error(error))
})
}, []);
console.log(dataLoc);
I was following this example: https://reactjs.org/docs/faq-ajax.html
But my code is returning weather.map is not a function?
function App(props) {
const [weather, setWeather] = useState([]);
useEffect(() => {
fetch("https://api.openweathermap.org/data/2.5/weather?q=kalamazoo&appid=XXXXXXXXXXXX")
.then(res => res.json())
.then(
(result) => {
setWeather(result)
console.log(result)
}
)
},[])
return (
<div className="App">
{weather.map(item => (
<li key={item.id}>{item.main}</li>
))}
</div>
);
}
export default App;
I understand that it expects an array, but even if the API hasn't returned I still get the error.
The openweathermap GET /weather API returns an object. You can check out on their Swagger for details of the APIs and their exact response formats
To access the weather information, you need to do the following:
useEffect(() => {
fetch(
'https://api.openweathermap.org/data/2.5/weather?q=kalamazoo&appid=XXXXXXXXXXXX'
)
.then((res) => res.json())
.then((result) => {
setWeather(result.weather); // this is where weather data array is present
console.log(result);
});
}, []);
map is not a function error mean that that weather data type is not an array so it hasn't a map function
the API returning an Object so instead of direct map you can use
Object.values(weather || {}).map((item)=>{
return(
<li key={item.id}>{item.main}</li>
)
})
I was trying to fetch api with react.js but on first render its gives nothing and the second render its gives data. This makes it so when I try to access the data later for an image I get an error, TypeError: Cannot read property 'news.article' of undefined, because it is initially empty. how can I solve this?
here is my code ..
import React, { useEffect, useState } from 'react';
const HomeContent = () => {
const [news, updateNews] = useState([]);
console.log(news);
useEffect(() => {
const api = 'http://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=940c56bd75da495592edd812cce82149'
fetch(api)
.then(response => response.json())
.then(data => updateNews(data))
.catch((error) => console.log(error))
}, [])
return (
<>
</>
);
};
export default HomeContent;
There is no issue with the code itself, the output you receive is expected. However, you can render the content after it is retrieved as such
import React, { useEffect, useState } from 'react';
const HomeContent = () => {
const [news, updateNews] = useState([]);
const [isLoading, setIsLoading] = useState(true);
console.log(news);
useEffect(() => {
const api = 'http://newsapi.org/v2/top-headlines?country=us&category=business&apiKey=940c56bd75da495592edd812cce82149'
fetch(api)
.then(response => response.json())
.then(data => {
updateNews(data.articles);
setIsLoading(false);
})
.catch((error) => {
console.log(error);
setIsLoading(false);
})
}, [])
return (
<>
{isLoading ?
<p>Loading...</p> :
// Some JSX rendering the data
}
</>
);
};
export default HomeContent;
I've been trying to update a function once data has been recieved and set to a state (using useState). After that the function will use the .map function display the data into a template.
However i am getting two errors, one is 'projects.map is not a function' (btw projects is my state name, where data is stored) and inside the useEffect function which updates when projects is changed 'Expected an assignment or function call and instead saw an expression'
import React, { useState, useEffect } from 'react';
import ProjectSummary from './projectSummary';
function ProjectList() {
// setting my state
const [projects, setProjects] = useState([])
// getting the data from some dummy online data when the app starts
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(data => setProjects({ data }))
}, []);
// makeing a function call postList, which stores a ternery operator
const postList = () => {
// The ternery operator asks if there is anything inside the porjects state
projects.length ? (
// If there is something in the state, it will map out the JSON array in the 'projectSummary template
projects.map(projects => {
return(
<div >
<ProjectSummary key={projects.id} title={projects.title} author={projects.userId} date='30 september, 2019' content={projects.body}/>
</div>
)
})
) : (
// If there isnt anything in the state is prints out 'Loading Data'
<h1>Loading Data</h1>
);
}
// useEffect updates when the 'projects' stae is updated (like componentDidUpdate, and runs the function again
useEffect(() => {
postList()
}, [projects]);
return(
<div className="ProjectList">
// The component should output the postList function, which should map out the array, in the template
{ postList }
</div>
)
}
export default ProjectList
You need to make some corrections to your component
import React, { useState, useEffect } from 'react';
import ProjectSummary from './projectSummary';
function ProjectList() {
const [projects, setProjects] = useState([])
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(data => setProjects(data))
}, []);
return(
<div className="ProjectList">
{
projects.length ?
projects.map(projects => (
<div>
<ProjectSummary key={projects.id} title={projects.title} author={projects.userId} date='30 september, 2019' content={projects.body} />
</div>
))
:
<h1>Loading Data</h1>
}
</div>
)
}
export default ProjectList;
You don't need the postList function and the second useEffect.
You may want to add additional checks to determine when the posts are loading and when they're empty after loading is done, so you don't just get a loading message
try this
const [projects, setProjects] = useState([])
and
.then(data => setProjects(data))
assuming that the data is an array
First you have to set the initial projects to an empty array like this:
const [projects, setProjects] = useState([])
, because currently projects is an empty string, which does not have the map function.
For the fetch, you should write it like this:
useEffect(async () => {
const data = await fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
setProjects(data);
}, []);
Hope this helps.
Array.prototype.map() is for Array.
const [projects, setProjects] = useState('');
projects not an array.