React - .map returning not a function? - javascript

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

Related

Load props before using to set state in a useEffect hook

I am trying to pass a user address into this Fetch Function, set the value of a state variable equal to the address, and then use that address to make an api call. But as expected, everything runs at the same time and the api call fails because it does not receive the user address.
I am relatively new to useEffect, the below is how I assume a function like this should be written, but evidently I am missing something. It does not return any errors, just a undefined value in the log statement I have below.
const Fetch = (props) => {
const api_key = process.env.REACT_APP_API_KEY;
const [addr,setAddr] = useState([])
const [data,setData] = useState([])
useEffect(() => {
async function Get(){
setAddr(props.useraddress)
}
Get();
}, []);
async function GetNFT() {
useEffect(() => {
axios
.get(
`https://flow-testnet.g.alchemy.com/v2/${api_key}/getNFTs/?owner=${addr}&offset=0&limit=10`
)
.then(res=> {
setData(res.data.nfts);
})
.catch(err=> {
console.log(err);
})
},[]);
}
GetNFT();
console.log(data);
return (
<div>
<script>{console.log('Fetch'+addr)}</script>
{/*
<>
{data.map((dat,id)=>{
return <div key={id}>
<FetchData NFTData={dat} />
</div>
})}
</>
*/}
</div>
)
}
You need a single useEffect that would depend on useraddress that you can destructure from the props, and make an api call that uses the useraddress. You don't need to store useraddress in the state.
const api_key = process.env.REACT_APP_API_KEY
const createUrl = addr => `https://flow-testnet.g.alchemy.com/v2/${api_key}/getNFTs/?owner=${addr}&offset=0&limit=10`
const Fetch = ({ useraddress }) => {
const [addr,setAddr] = useState([])
const [data,setData] = useState([])
useEffect(() => {
axios.get(createUrlcreateUrl(useraddress))
.then(res=> {
setData(res.data.nfts)
})
.catch(err=> {
console.log(err)
})
}, [useraddress])
console.log(data)
return (
// jsx
)
}
Note that the useEffect would be triggered on component's mount, and whenever useraddress changes. If useraddress might be empty or undefined when the component mounts, add a condition inside that avoids the call:
useEffect(() => {
if(!useraddress) return // skip the api call if the address is empty/undefined/null
axios.get(createUrlcreateUrl(useraddress))
.then(res => {
setData(res.data.nfts)
})
.catch(err => {
console.log(err)
})
}, [useraddress])

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

API data not appearing in component but shows in console

I am making a rather simple API call to (https://api.punkapi.com/v2/beers) and displaying fetched data in the component, but somehow the data is not getting displayed on the page, but when I console log it the data is displayed correctly.
const Comp= () => {
const [item, setItem] = React.useState([]);
React.useEffect(() => {
fetch('https://api.punkapi.com/v2/beers')
.then((res) => res.json())
.then((data) => {
setItem(data);
})
.catch((err) => {
console.log(err);
});
}, []);
return (
<div>
{item.map((beer) => {
const { name, tagline } = beer;
<p>{name}</p>;
console.log(name);
})}
</div>
);
};
Issue
You don't return anything from your item mapping. Return the JSX you want to render (i.e. return <p>{name}</p>) and don't forget to add a React key to the mapped elements. Below I've simplified to an implicit return.
You should also remove the console log from the render return as "render" methods are to be pure functions without side-effects, such as console logging. Either log the fetch result in the promise chain or use an useEffect hook to log the updated state.
useEffect(() => console.log(item), [item]);
...
return (
<div>
{item.map(({ id, name }) => (
<p key={id}>{name}</p>
))}
</div>
);
You need to return the value from inside .map method like:
return (<p>{name}</p>)
You need to return the JSX elements in your map.
const Comp= () => {
const [item, setItem] = React.useState([]);
React.useEffect(() => {
fetch('https://api.punkapi.com/v2/beers')
.then((res) => res.json())
.then((data) => {
setItem(data);
})
.catch((err) => {
console.log(err);
});
}, []);
return (
<div>
{item.map((beer) => {
const { name, tagline } = beer;
return <p>{name}</p>;
console.log(name);
})}
</div>
);
};

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

What is the problem in React hook when retrieve data from API

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!

Categories

Resources