TypeError: weatherData.map is not a function - reactjs

I'm trying to map over data from API, but while writing the code to display the data I got this error: TypeError: weatherData.map is not a function
I tried removing useEffect from the code and tried to add curly brackets: const [weatherData, setWeatherData] = useState([{}])
Update: Line 14 log undefined : console.log(weatherData.response)
import axios from 'axios'
import { useEffect, useState } from 'react'
import './App.css'
function App() {
const [search, setSearch] = useState("london")
const [weatherData, setWeatherData] = useState([])
const getWeatherData = async () => {
try {
const weatherData = await axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${search}&appid={APIKEY}`);
console.log(weatherData.response);
if (weatherData) {
setWeatherData(weatherData);
}
} catch (err) {
console.error(err);
}
}
useEffect(() => {
getWeatherData()
}, [getWeatherData])
const handleChange = (e) => {
setSearch(e.target.value)
}
return (
<div className="App">
<div className='inputContainer'>
<input className='searchInput' type="text" onChange={handleChange} />
</div>
{weatherData.map((weather) => {
return (
<div>
<h1>{weather.name}, {weather.country}</h1>
</div>
)
})}
</div>
)
}
export default App

You're having errors in fetching the data as well as rendering it.
Just change the entire App component like this :
import { useEffect, useState } from "react";
import axios from "axios";
function App() {
const [search, setSearch] = useState("London");
const [weatherData, setWeatherData] = useState([]);
const APIKEY = "pass your api key here";
useEffect(() => {
const fetchData = async () => {
const result = await axios(
`https://api.openweathermap.org/data/2.5/weather?q=${search}&appid=${APIKEY}`
);
setWeatherData(result.data);
};
fetchData();
}, [search]);
const handleChange = (e) => {
setSearch(e.target.value);
};
return (
<div className="App">
<div className="inputContainer">
<input className="searchInput" type="text" onChange={handleChange} />
</div>
<h1>
{" "}
{weatherData.name} ,{" "}
{weatherData.sys ? <span>{weatherData.sys.country}</span> : ""}{" "}
</h1>
</div>
);
}
export default App;
this should be working fine just make sure to change : const APIKEY = "pass your api key "; to const APIKEY = "<your API key> ";
this is a demo in codesandbox

Create a promise function:
const getWeatherData = async () => {
try {
const weatherData = await axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${search}&appid={APIKEY}`);
console.log(weatherData.response);
if (weatherData.response.data) {
setWeatherData(weatherData.response.data);
}
} catch (err) {
console.error(err);
}
}
Then call it.

Related

How to add debouncing to my simple search query?

Here is a very basic search by title query on a public API. In my actual app my API calls are under services.js files so I'm trying to do this where my API call is not inside the react component.
https://codesandbox.io/s/elastic-pond-pghylu?file=/src/App.js
import * as React from "react";
import axios from "axios";
// services.js
const fetchPhotos = async (query) => {
const { data } = await axios.get(
`https://jsonplaceholder.typicode.com/photos?title_like=${query}`
);
return data;
};
export default function App() {
const [photos, setPhotos] = React.useState([]);
const [searchTerm, setSearchTerm] = React.useState("");
const fetch = React.useCallback(async () => {
const data = await fetchPhotos(searchTerm);
setPhotos(data);
}, [searchTerm]);
React.useEffect(() => {
fetch();
}, [fetch]);
return (
<div>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<div>
{photos?.map((photo) => (
<div>{JSON.stringify(photo.title)}</div>
))}
</div>
</div>
);
}
The problem with my code is this (too many api calls while im typing):
My attempt to fix this
I tried cancelToken. This cancels my previous request. I was able to implement this but in my actual API the get request is so fast that it still manages to finish the request. So I'm trying to do this without cancelToken.
I recently came across debouncing and it seems to do what I need i'm just struggling to get it to work
for example, I tried this: (I wrapped debounce onto my fetchPhotos function)
import {debounce} from 'lodash';
// services.js
const fetchPhotos = debounce(async (query) => {
const { data } = await axios.get(
`https://jsonplaceholder.typicode.com/photos?title_like=${query}`
);
return data;
}, 500);
however now fetchphotos returns undefined always?
You can make use of useCallback so that the debounced (fetchPhotos) will have same function across re-renders
import * as React from "react";
import axios from "axios";
import { debounce } from "lodash";
// services.js
export default function App() {
const [photos, setPhotos] = React.useState([]);
const [searchTerm, setSearchTerm] = React.useState("");
async function fetchData(searchTerm) {
const data = await axios.get(
`https://jsonplaceholder.typicode.com/photos?title_like=${searchTerm}`
);
setPhotos(data.data);
}
const debounced = React.useCallback(debounce(fetchData, 500), []);
React.useEffect(() => {
// for the first render load
fetchData("");
}, []);
return (
<div>
<input
type="text"
value={searchTerm}
onChange={(e) => {
setSearchTerm(e.target.value);
debounced(e.target.value, 1000);
}}
/>
<div>
{photos?.map((photo) => (
<div key={photo.id}>{JSON.stringify(photo.title)}</div>
))}
</div>
</div>
);
}

Firebase image uploads two times

I am trying to create gallary app and when i try to upload image in fire storage there is only one image but in fire store there are two entries for same image so i think it is happening because it uploads image two times i try to figure out but nothing is working
Here is my use storage hook
import React, { useState, useEffect } from 'react'
import { fireStorage, fireStore } from '../firebase-config';
import { collection, addDoc, serverTimestamp } from "firebase/firestore";
import { ref, getDownloadURL, uploadBytesResumable } from "firebase/storage";
export default function useStorage(file) {
const [progresspercent, setProgresspercent] = useState(0);
const [error, setError] = useState(null);
const [url, setImgUrl] = useState(null);
useEffect(() => {
const storageRef = ref(fireStorage, `files/${file.name}`);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on("state_changed",
(snapshot) => {
const progress =
Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
setProgresspercent(progress);
},
(error) => {
setError(error);
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
setImgUrl(downloadURL)
addDoc(collection(fireStore, 'images'),{
url: downloadURL,
createdAt: serverTimestamp()
})
});
}
);
},[file]);
return { progresspercent, url, error};
}
Here is my upload form
import { useState } from "react";
import ProgressBar from './ProgressBar'
function UploadForm() {
const [file, setFile] = useState(null);
const [error, setError] = useState("");
const allowedType = ["image/png", "image/jpg", "image/jpeg"];
const changeHandler = (e) => {
e.preventDefault()
let selectedFile = e.target.files[0]
if (selectedFile && allowedType.includes(selectedFile.type)){
setFile(selectedFile);
setError('')
}else{
setFile(null);
setError("Please select an image file");
}
};
return (
<form>
<label>
<input type="file" onChange={changeHandler} />
<span>+</span>
</label>
<div className="output">
{error && <div className="error">{error}</div>}
{file && <div>{file.name}</div>}
{file && <ProgressBar file={file} setFile={setFile} />}
</div>
</form>
);
}
export default UploadForm;
and Here is my progress bar
import React, { useEffect } from "react";
import useStorage from "..//../src/Hooks/useStorage";
export default function ProgressBar({ file, setFile }) {
const { url, progresspercent } = useStorage(file);
useEffect(() => {
if (url) {
setFile(null);
}
}, [url, setFile]);
return (
<div
className="progress-bar"
style={{ width: progresspercent + "%" }}
></div>
);
}
I don't have any other component, i try to fix it but i don't know why it is happening at first place.

ReactJS Error when using map function, cannot read properties of undefined

I'm trying to make a sport/tinder like app for a school project from a friend of mine. It came together well on my localhost, but for him it was a requirement to host it online. Not really a professional in hosting, but I was a bit familiar with Heroku. I used a client and a server side for my application, so I build the client side and put it into the server side folder. This server side is hosted on the Heroku page. But whenever I try to login, it won't work and I get this error message in my console.
TypeError: Cannot read properties of undefined (reading 'map')
The error says it is caused by this line of code.
const matchedUserIds = matches.map(({user_id}) => user_id)
This is the whole MatchDisplay file that is used in my Dashboard. I'm using a MongoDB for the storage of my users.
import axios from "axios";
import { useEffect, useState } from "react";
import { useCookies } from "react-cookie";
const MatchesDisplay = ({ matches, setClickedUser }) => {
const [matchedProfiles, setMatchedProfiles] = useState(null);
const [cookies, setCookie, removeCookie] = useCookies(null);
const [matched, setMatched] = useState(null);
const matchedUserIds = matches.map(({ user_id }) => user_id);
const userId = cookies.UserId;
const getMatches = async () => {
try {
const response = await axios.get(
"https://[app].herokuapp.com/users",
{
params: { userIds: JSON.stringify(matched()) },
}
);
setMatchedProfiles(response.data);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getMatches();
}, [matches]);
const filteredMatchedProfiles = matchedProfiles?.filter(
(matchedProfile) =>
matchedProfile.matches.filter(
(profile) => profile.user_id === userId
).length > 0
);
return (
<div className="matches-display">
{filteredMatchedProfiles?.map((match) => (
<div
key={match.user_id}
className="match-card"
onClick={() => setClickedUser(match)}
>
<div className="img-container">
<img
src={match?.url}
alt={match?.first_name + "profile"}
/>
</div>
<h3>{match?.first_name}</h3>
</div>
))}
</div>
);
};
export default MatchesDisplay;
Any help is welcome. If you need more code examples, please reply ;)
EDIT
The ChatContainer that passes the user to the MatchesDisplay.
import ChatHeader from "./ChatHeader";
import MatchesDisplay from "./MatchesDisplay";
import ChatDisplay from "./ChatDisplay";
import { useState } from 'react';
const ChatContainer = ({user}) => {
const [ clickedUser, setClickedUser] = useState(null)
return (
<div className="chat-container">
<ChatHeader user={user}/>
<div>
<button className="option" onClick={() => setClickedUser(null)}>Matches</button>
<button className="option" disabled={!clickedUser}>Chat</button>
<button className="option" >Prices</button>
</div>
{!clickedUser && <MatchesDisplay matches={user.matches} setClickedUser={setClickedUser}/>}
{clickedUser && <ChatDisplay user={user} clickedUser={clickedUser}/>}
</div>
)
}
export default ChatContainer
The Dashboard that passes the user to the Chatcontainer.
import TinderCard from 'react-tinder-card';
import {useEffect, useState} from 'react';
import {useCookies} from 'react-cookie';
import ChatContainer from '../components/ChatContainer'
import axios from "axios";
const Dashboard = () => {
const [user, setUser] = useState(null)
const [genderedUsers, setGenderedUsers] = useState(null)
const [lastDirection, setLastDirection] = useState(null)
const [cookies, setCookie, removeCookie] = useCookies(['user'])
const [matchedUserIds, setMatchedUserIds] = useState(null)
const [filteredGenderedUsers, setFilteredGenderedUsers] = useState(null)
const userId = cookies.UserId
const getUser = async () => {
try {
const response = await axios.get('https://funfit-webpage.herokuapp.com/user', {
params: {userId}
})
return setUser(response.data)
} catch (error) {
console.log(error)
}
}
const getGenderedUsers = async () => {
try {
const response = await axios.get('https://funfit-webpage.herokuapp.com/gendered-users', {
params: {gender: user?.gender_interest}
})
return setGenderedUsers(response.data)
} catch (error) {
console.log(error)
}
}
useEffect(() => {
getUser()
}, [])
useEffect(() => {
setMatchedUserIds(user?.matches.map(({user_id}) => user_id).concat(userId))
if (user) return getGenderedUsers()
}, [user])
useEffect(() => {
if (genderedUsers) {
return setFilteredGenderedUsers(genderedUsers?.filter(
genderedUser => !matchedUserIds.includes(genderedUser.user_id)
))
}
}, [genderedUsers])
const updateMatches = async (matchedUserId) => {
try {
await axios.put('https://funfit-webpage.herokuapp.com/addmatch', {
userId,
matchedUserId
})
return getUser()
} catch (error) {
console.log(error)
}
}
const swiped = (direction, swipedUserId) => {
console.log(direction, swipedUserId)
if (direction === 'right') {
updateMatches(swipedUserId)
}
return setLastDirection(direction)
}
const outOfFrame = (name) => {
console.log(name + ' left the screen!')
}
return (<>
{user && <div className="dashboard">
<ChatContainer user={user}/>
<div className="swipe-container">
<div className="card-container">
{filteredGenderedUsers?.map((genderedUser) =>
<TinderCard
className='swipe'
key={genderedUser.user_id}
onSwipe={(dir) => swiped(dir, genderedUser.user_id)}
onCardLeftScreen={() => outOfFrame(genderedUser.first_name)}>
<div style={{backgroundImage: 'url(' + genderedUser.url + ')'}} className='card'>
<h3>{'Name: ' + genderedUser.first_name} <br/> {'Sport: ' + genderedUser.about}</h3>
</div>
</TinderCard>)}
<div className="swipe-info">
{lastDirection ? <p>You swiped {lastDirection}</p> : <p/>}
</div>
</div>
</div>
</div>}
</>)
}
export default Dashboard

Avoiding React Race condition with AbortController not working

i am trying to mimic React useEffect race condition and handle that with AbortController. I can never hit the catch block ( i guess ) because the setTimeOut is called post the fetch request. My question is how can i rewrite this code to put fetch inside setTimeout and still be able to use AbortController to cancel the request?
import './App.css';
import {useState,useEffect} from 'react'
function App() {
const [country, setCountry] = useState('N.A.')
const [capital, setCapital] = useState('')
useEffect(() => {
const ctrl = new AbortController();
const load = async() =>{
try
{
//debugger
const response = await fetch(`https://restcountries.eu/rest/v2/capital/${capital}`,
{signal:ctrl.signal})
const jsonObj = await response.json()
setTimeout( ()=> {setCountry(jsonObj[0].name)} , Math.random()*10000)
}
catch(err)
{
console.log(err)
}
}
load();
return () =>{
ctrl.abort()
};
}, [capital])
return (
<div>
<button onClick={()=>setCapital("Berlin")} >Berlin</button>
<button onClick={()=>setCapital("Paris")} >Paris</button>
<button onClick={()=>setCapital("Madrid")} >Madrid</button>
<div>
{country}
</div>
</div>
);
}
export default App;
Hmm... just put that function inside setTimeout calling and don't forget to clean up the timer on unmount (Demo).
import React, { useState, useEffect } from "react";
export default function TestComponent(props) {
const [country, setCountry] = useState("N.A.");
const [capital, setCapital] = useState("");
const [error, setError] = useState(null);
useEffect(() => {
let isMounted = true;
const ctrl = new AbortController();
const timer = setTimeout(async () => {
try {
if (!capital) {
return;
}
const response = await fetch(
`https://restcountries.eu/rest/v2/capital/${capital}`,
{ signal: ctrl.signal }
);
// if (!isMounted) return; // can be omitted here
const jsonObj = await response.json();
isMounted && setCountry(jsonObj[0].name);
} catch (err) {
console.log(err);
isMounted && setError(err);
}
}, Math.random() * 10000);
return () => {
clearTimeout(timer);
isMounted = false;
ctrl.abort();
};
}, [capital]);
return (
<div className="component">
<div className="caption">useAsyncEffect demo:</div>
<button onClick={() => setCapital("Berlin")}>Berlin</button>
<button onClick={() => setCapital("Paris")}>Paris</button>
<button onClick={() => setCapital("Madrid")}>Madrid</button>
<div>Country: {error ? <b>{error.toString()}</b> : country}</div>
</div>
);
}
Or you can do the same with custom libs (Demo):
import React, { useState } from "react";
import { useAsyncEffect, E_REASON_UNMOUNTED } from "use-async-effect2";
import { CPromise, CanceledError } from "c-promise2";
import cpFetch from "cp-fetch";
export default function TestComponent(props) {
const [country, setCountry] = useState("N.A.");
const [capital, setCapital] = useState("");
const [error, setError] = useState(null);
const cancel = useAsyncEffect(
function* () {
setError(null);
if (!capital) {
return;
}
yield CPromise.delay(Math.random() * 10000);
try {
const response = yield cpFetch(
`https://restcountries.eu/rest/v2/capital/${capital}`
).timeout(props.timeout);
const jsonObj = yield response.json();
setCountry(jsonObj[0].name);
} catch (err) {
CanceledError.rethrow(err, E_REASON_UNMOUNTED);
console.log(err);
setError(err);
}
},
[capital]
);
return (
<div className="component">
<div className="caption">useAsyncEffect demo:</div>
<button onClick={() => setCapital("Berlin")}>Berlin</button>
<button onClick={() => setCapital("Paris")}>Paris</button>
<button onClick={() => setCapital("Madrid")}>Madrid</button>
<button onClick={cancel}>Cancel request</button>
<div>Country: {error ? <b>{error.toString()}</b> : country}</div>
</div>
);
}

getting "Cannot read property "map" of undefined

I am getting "Cannot read property "map" of undefined (it is referring to tradeData) from the TradeDataList.js file. I am calling an API that is called through search term that is passed to "unirest" get function. I have setup context API passing some centralized values to various other files. I have it set up as an array in the useState. I am loosing my head with this if anyone can help please. The files I have are the following:
context.js
import React, { useState, useContext, useEffect } from 'react'
import { useCallback } from 'react'
var unirest = require('unirest');
const url = 'https://api.someurl.com/oauth/client_credential/accesstoken' ;
const Browse_URL = 'https://api.someurl.com/tradesregister/v1/browse' ;
const MY_API_KEY = 'EpVhCPGX4lzMwzdXVKG7yYFubGtwmlYU4343434';
const Authorization = 'Basic RXBWaENQR1g0bHpNd3pkWFZLRzd5WUZ1Ykd0d21sWVU6M2NvcEVuQTVEZlJXZ3BSYw==' ;
const AppContext = React.createContext()
const AppProvider = ({ children }) => {
const [token, setToken] = useState('') ;
const [loading, setLoading] = useState(true) ;
const [tradeData, setTradeData] = useState([]) ;
const [searchTerm, setSearchTerm] = useState('')
const getToken = () => {
unirest.get(url)
.header({'Accept': 'application/json', 'authorization': Authorization})
.query({"grant_type": "client_credentials"})
.end(function (response) {
const token = response.body["access_token"] ;
//console.log(token) ;
setToken(token)
})
}
useEffect (() => {
// call the getToken function
getToken() ;
}, [])
//Get the new token to access trade data
const newAuth = 'Bearer ' + token
return (
<AppContext.Provider
value={{ token,
MY_API_KEY,
newAuth,
Browse_URL,
loading,
setTradeData,
tradeData,
setLoading,
searchTerm,
setSearchTerm
}}
>
{children}
</AppContext.Provider>
)
}
export const useGlobalContext = () => {
return useContext(AppContext)
}
export { AppContext, AppProvider }
SearchForm.js (call starts from here )
import React, { useEffect, useState } from 'react'
import { useGlobalContext } from '../context'
import TradeDataList from './TradeDataList'
//import Loading from './Loading'
var unirest = require('unirest');
const SearchForm = () => {
const {searchTerm, setSearchTerm,loading, setLoading, tradeData, setTradeData, MY_API_KEY, newAuth, Browse_URL } = useGlobalContext();
//const [tradeData, setTradeData] = useState([]) ;
const getTrade = () => {
setLoading(true)
unirest.get(Browse_URL)
.header({'Accept': 'application/json', 'authorization': newAuth, "apikey": MY_API_KEY })
.query({"searchText": searchTerm })
.end(function (response) {
const tradeData = response.body ;
//console.log(tradeData) ;
setTradeData(tradeData)
setLoading(false);
})
}
useEffect (() => {
// call the getTrade function
getTrade() ;
}, [])
const handleSubmit = (e) => {
e.preventDefault() ;
getTrade() ;
}
console.log('here is the data' ,tradeData)
return (
<section className="section-search">
<form className="search-form" onSubmit={handleSubmit}>
<div className="form-control">
<label htmlFor='searchTerm'> search trade licence info</label>
<input
type='text'
id='searchTerm'
//ref={searchValue} //reference
value={searchTerm}
//onChange={searchTrade}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<button type="submit">Submit</button>
</form>
<div className="cocktails-center">
<TradeDataList tradeData={tradeData} />
</div>
</section>
)
}
export default SearchForm
TradeList.js
import React from 'react'
import TradeData from './TradeData'
//import Loading from './Loading'
//import { useGlobalContext } from '../context'
/**
* This page displays TradeData and loading
*/
const TradeDataList = ({ tradeData }) => {
//display loading while cocktails are being loaded
return (
<section className="section">
<h2 className="section-title">
Trade Data
</h2>
{/* <h2>cocktail list</h2> */}
<div className="cocktails-center">
{tradeData.map((trade) => {
// this is to be handled by Cocktail component
return <TradeData key={trade.licenceID} {...trade} />
})}
</div>
</section>
)
}
export default TradeDataList
TradeData.js
import React from 'react'
import { Link } from 'react-router-dom'
//image,name,id,info,glass
const TradeData = ({
licenceID,
licensee,
licenceName,
licenceNumber,
licenceType,
status,
suburb,
postcode,
businessNames,
categories,
classes
}) => {
return (
<article className="cocktail">
<div className="img-container">
<h1>This is header </h1>
</div>
<div className="cocktail-footer">
<h3>{licenceID}</h3>
<p>{licensee}</p>
<h4>{licenceName}</h4>
<p>{licenceType}</p>
{/* <Link to={`/cocktail/${id}`} className="btn btn-primary
btn-details">details</Link> */}
</div>
</article>
)
}
export default TradeData
Inside your TradeList.js you simply need to add a check condition:
<div className="cocktails-center">
{tradeData?tradeData.map((trade) => {
// this is to be handled by Cocktail component
return <TradeData key={trade.licenceID} {...trade} />
}):null}
</div>
Another alternative solution
{ tradeData && tradeData.map((trade) => {
// this is to be handled by Cocktail component
return <TradeData key={trade.licenceID} {...trade} />
})}
Note: The .map function is only available on array.
If data isn't in the format you are expecting it to be (it is {} but you are expecting []).

Resources