Array data not mappable even though console shows data - javascript

I am having a small issue that I cannot seem to get right.
I am fetching data from Pokeapi to get some pokemon back, then iterating through that data in order to trigger another fetch request to bring back the associated data with a given pokemon.
I want to push this data to an array so that I can fire off this request as the application loads and save the data to storage so no new requests have to be made. However... (Which I have not done yet btw)
I console.log the results and the data is there, but when trying to map the data out, nada.
Please see my attempt below:
export function SomeComponent() {
let arr = [];
async function fetchPokemon() {
const pokemon = await fetch('https://pokeapi.co/api/v2/pokemon?limit=10')
.then(response => response.json())
for await (const x of pokemon.results.map(async (result) => await fetch(result.url)
.then(response => response.json()))) {
arr.push(x);
}
}
fetchPokemon();
console.log(arr)
return (
<>
{arr.map((pokemon) => (
<p>
{pokemon.name}
</p>
))}
</>
)
}
My best guess is that the console is displaying live results and that they data is not actually populated yet?
I would like to see the pokemon names displayed in the tag.

In react we should use state to store data and useEffect to do side effect actions like fetching data :
export function SomeComponent() {
const [arr, setArray] = useState([]);
async function fetchPokemon() {
const pokemon = await fetch('https://pokeapi.co/api/v2/pokemon?limit=10')
.then(response => response.json())
for await (const x of pokemon.results.map(async (result) => await fetch(result.url)
.then(response => response.json()))) {
setArray([...arr, x]);
}
}
useEffect(() => {fetchPokemon()}, []}
return (
<>
{arr.map((pokemon) => (
<p>
{pokemon.name}
</p>
))}
</>
)
}

Related

Why I getting an error while trying to use map on data that i get using hook?

I trying map data which i get using custom hook that recives data from my "storage" (which is actually a class with array of objects and async function to get them) i reciving an error: Cannot read properties of undefined (reading 'map').
Code of hook:
const useCreations = () => {
const service = new aboutPisanka();
const [data, setData] = useState();
useEffect(() => {
console.log(123 )
service.getAllCreations().then((data) => setData(data));
}, []);
return data
};
Code of element:
const data = useCreations();
console.log(data);
const cards = data.map((data) => {
const { heading, text, id, image } = data;
return (
<div key={id}>
<img src={require(image)}/>
<h1>{heading}</h1>
<p>{text}</p>
</div>
);
});
Code of function in class where data stores:
getAllCreations = async () => {
return this._creations;
};
(Sorry for my bad english)
As I understand it happens because it maps data before it gets to state but I don't have any idea how to fix it only making high order componen which will wrap my element.
Your data is initialize as undefined. So when the render runs before you assign an array value to data, your .map function fails because you call that on an undefined. Just initialize your data as an empty array.
const useCreations = () => {
const service = new aboutPisanka();
const [data, setData] = useState([]); // NOTE: Initialization as an empty array
useEffect(() => {
console.log(123 )
service.getAllCreations().then((data) => setData(data));
}, []);
return data
};
Also, you could use es6 de structuring to improve readability (subjective).
const data = useCreations();
console.log(data);
const cards = data.map(({ heading, text, id, image }) => (
<div key={id}>
<img src={require(image)}/>
<h1>{heading}</h1>
<p>{text}</p>
</div>
));
I do not see your whole code however if you are receiving data in correct format after a while because of Api response,you can add ? in cards const cards = data?.map((data) => { to fix that error

React - .map returning not a function?

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

Promise only resolves correctly on page refresh

I am playing around with an API that gets a list of Pokemon and corresponding data that looks like this.
export function SomePage() {
const [arr, setArray] = useState([]);
useEffect(() => {
fetchSomePokemon();
}, []);
function fetchSomePokemon() {
fetch('https://pokeapi.co/api/v2/pokemon?limit=5')
.then(response => response.json())
.then((pokemonList) => {
const someArray = [];
pokemonList.results.map(async (pokemon: { url: string; }) => {
someArray.push(await fetchData(pokemon))
})
setArray([...arr, someArray]);
})
}
async function fetchData(pokemon: { url: string; }) {
let url = pokemon.url
return await fetch(url).then(async res => await res.json())
}
console.log(arr);
return (
<div>
{arr[0]?.map((pokemon, index) => (
<div
key={index}
>
{pokemon.name}
</div>
))
}
</div>
);
}
The code works(kind of) however on the first render the map will display nothing even though the console.log outputs data. Only once the page has been refreshed will the correct data display. I have a feeling it's something to do with not handling promises correctly. Perhaps someone could help me out.
TIA
Expected output: Data populated on initial render(in this case, pokemon names will display)
The in-build map method on arrays in synchronous in nature. In fetchSomePokemon you need to return a promise from map callback function since you're writing async code in it.
Now items in array returned by pokemonList.results.map are promises. You need to use Promise.all on pokemonList.results.map and await it.
await Promise.all(pokemonList.results.map(async (pokemon: { url: string; }) => {
return fetchData.then(someArray.push(pokemon))
}));
On your first render, you don't have the data yet, so arr[0] doens't exist for you to .map on it, so it crashes. You need to check if the data is already there before mapping.
Using optional chaining, if there's no data it will not throw an error on your first render and it will render correctly when the data arrive and it re-renders.
...
return (
<div>
{arr[0]?.map((pokemon, index) => (
<div key={index}>{pokemon.name}</div>
))}
</div>
);
}
in
useEffect(() => { fetchSomePokemon(); }, []);
[] tells react there is no dependencies for this effect to happen,
read more here https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
One way to solve your issues is to await the data fetching in useEffect().
Here's a POC:
export function Page() {
const [pokemon, setPokemon] = useState([]);
// will fetch the pokemon on the first render
useEffect(() => {
async function fetchPokemon() {
// ... logic that fetches the pokemon
}
fetchPokemon();
}, []);
if (!pokemon.length) {
// you can return a spinner here
return null;
}
return (
<div>
{pokemon.map(item => {
// return an element
})}
</div>
);
}

ReactJS how to update page after fetching data

I'm new to ReactJS and I'm now trying to do an interactive comments section (taken from frontendmentor.io), but the App component just doesn't show what it's supposed to show
This is my App component:
function App() {
const [data, setData] = useState([]);
useEffect(() => {
const getComm = async () => {
await fetchData();
};
getComm();
}, []);
console.log(data);
const fetchData = async () => {
const res = await fetch("db.json").then(async function (response) {
const comm = await response.json();
setData(comm);
return comm;
});
};
return (
<Fragment>
{data.length > 0 ? <Comments data={data} /> : "No Comments to Show"}
</Fragment>
);
}
export default App;
The console.log(data) logs two times:
the first time it's an empty Array;
the second time it's the Array with my datas inside.
As it follows:
If I force the App to print the Comments it just says that cannot map through an undefined variable
This is my Comments component:
function Comments({ data }) {
return (
<div>
{data.map((c) => (
<Comment key={c.id} />
))}
</div>
);
}
export default Comments;
I'm wondering why the page still displays No Comments to Show even if the log is correct
#Cristian-Irimiea Have right about response get from fetch. Response is an a object and can't be iterate. You need to store in state the comments from response
But you have multiple errors:
Take a look how use async function. Your function fetchData looks bad.
// Your function
const fetchData = async () => {
const res = await fetch("db.json").then(async function (response) {
const comm = await response.json();
setData(comm);
return comm;
});
};
// How can refactor
// fetchData function have responsibility to only fetch data and return a json
const fetchData = async () => {
const response = await fetch("./db.json");
const body = await response.json();
return body;
};
You are updating state inside fetch function but a good solution is update state then promise resolve:
useEffect(() => {
// here we use .then to get promise response and update state
fetchData().then((response) => setData(response.comments));
}, []);
The initial state of your data is an array.
After you fetch your data from the response you get an object. Changing state types is not a good practice. You should keep your data state as an array or as an object.
Considering you will keep it as an array, you need use an array inside of setData.
Ex.
comm && Array.isArray(comm.comments) && setData(comm.comments);
As for your Comments component you should consider expecting an array not an object.
Ex.
function Comments(data) {
return (
<div>
{data.map((c) => (
<Comment key={c.id} />
))}
</div>
);
}
export default Comments;

Displaying data from api on react

I am trying to display the name of each of the teams using the following API:https://www.balldontlie.io/api/v1/teams. I have the following code in my main app.js file:
const result = await Axios('https://www.balldontlie.io/api/v1/teams')
console.log(result.data)
console.log(result.data.data[0])
This is successfully able to fetch the data and the first console line is able to display all data in the console while the second line displays all the information about the first team in the data. In each of the teams data information, they have one attribute that is called 'name' and says the team name. I was wondering on how I will be able to call this. When I have the following code in another file it doesn't display anything on the screen:
{Object.keys(items).map(item => (
<h1 key={item}>{item.data}</h1>
))}
What should I change item.data to be able to properly display the names of all the teams? I could provide more code if needed, but I thought this code would probably do.
Don't use axios, now JS has a better alternative called fetch. wrap call on a async function. finally destructor the data object. and void installing more things on your node_modules.
What's here ?
Request endpoint using callApi function
Collect only data from all json scope when finish promise.
Loop over each name
const myComponent = () => {
const names = [];
const callApi = async () => {
await fetch('https://www.balldontlie.io/api/v1/teams')
.then(r => r.json())
.then(resp => { const { data } = resp; return data; })
.catch(e => { console.log(e.message); });
};
callApi();
return <>
{names && names.length > 0
? <span>{names.map(({ id, name }) => <h1 key={id}>{name}</h1>)}</span>
: <span>No rows </span>}
</>;
According to your second block of code, you are trying to access a "data" property of a string, since you are mapping an array of keys of the items.
Effectively:
const item_keys = Object.keys(items);
//if the items is an array, item_keys = ["0","1",...]
//if items is an object, item_keys = ["id","name",...]
const result = item_keys.map(item => (
<h1 key={item}>{item.data}</h1>
));
//either way, result is an array of <h1>undefined</h1>
//because item is a string
Assuming you defined items as const items = result.data.data (edited), you should be able to display names like this:
{items.map((item, index) => (
<h1 key={index}>{item.name}</h1>
))}
const result = await Axios('https://www.balldontlie.io/api/v1/teams')
const data = result.data;
//if you want to display all names
{
data.map(team => (
<div>
<h1 key={`${team.id}`}>{team.name}</h1>
</div>
)
}
//if you want to display all fields in each team
{
data.map(team => (
<div key={`${team.id}`}>
{
Object.keys(team).map((key, keyIndex) => (
<h1 key={`k-${team.id}-${keyIndex}`}>{team[`${key}`]}</h1>
))
}
</div>
)
}
Your response is an array
// you need to change this
Object.keys(items).map(item => (
<h1 key={item}>{item.data}</h1>
))}
// to this where items = result.data.data
items.map(item => <h1 key={item.id}>{item.city} </h1>
// to note here item.city need to be a string or number and not an objet
//if your data does not have an Id
items.map((item, index) => <h1 key={index}>{item.city} </h1>
and in your code this will become somthing like this
return (
<div>
{items.map((item, index) => <h1 key={index}>{item.city} </h1>}
</div>
)
Here an example how the complete code should looks, since you are seeing the response and data in console but not in the UI, it could be a state problem.
const App = () => {
const [items, setItems] = useState([]);
useEffect(()=>{
fetch('https://www.balldontlie.io/api/v1/teams')
.then(res => res.json())
.then(res => {
setItems(res.data);
})
.catch(error => {
//handle error here
});
},[])
return (
<>
{items.map(el => <h1 key={el.id}> el.city </h1>}
</>
)
}
As your data response is an array of objects and your requirement is to display attributes of that data, you can just run a simple loop on data and render items. Below "data" is the response from API
const data = [{"id":"1",name:"Team1"}, {"id":"2",name:"Team2"}]
data.map(item =>
<div>
<h1 key={id}>{item.name}</h1>
</div>
);

Categories

Resources