Getting undefined value in useEffect (React) - reactjs

The below function gets the current location of a user:
const getCurrentLocation = () => {
fetch("https://ipinfo.io/json?token=$TOKEN")
.then((response) => response.json())
.then((jsonResponse) => {
console.log(jsonResponse)
return jsonResponse;
});
};
useEffect(() => {
console.log(getCurrentLocation());
}, []);
logging in useEffect is showing undefined and it is appearing first in the console, then jsonResponse shows next in the console with the corresponding object. Why is that ?

getCurrentLocation doesn't return anything, that's why you got undefined.
Moreover, fetch returns a Promise, which is asynchronous, meaning you don't get the result immediately, you must pass a calback to then to get the result when it is available.
const getCurrentLocation = () => {
return fetch("https://ipinfo.io/json?token=$TOKEN")
.then(response => response.json());
};
useEffect(() => {
getCurrentLocation()
.then(location => console.log(location));
}, []);

The getCurrentLocation function is not returning anything. Try saving the location in the state, so that you can access it when needed:
const [currentLocation, setCurrentLocation] = useState(null);
const getCurrentLocation = () => {
fetch("https://ipinfo.io/json?token=$TOKEN")
.then((response) => response.json())
.then((jsonResponse) => {
setCurrentLocation(jsonResponse); // <- save the location in state
});
};
useEffect(() => {
getCurrentLocation();
}, []);
return <div>{currentLocation}</div>
If you need the location in a useEffect, you could do:
useEffect(() => {
if (currentLocation !== null) {
// ...
}
}, [currentLocation])

you can simply use async/await to get response, take a look at this one:
const getCurrentLocation = async () => {
const result = await fetch("https://ipinfo.io/json?token=$TOKEN");
return result.json();
};
const handleGetLocation = async () => {
const result = await getCurrentLocation();
console.log(result);
};
useEffect(() => {
handleGetLocation();
}, []);

Related

React JS + Axios return undefined first

I trying make an axios get from context file into function and call this from component to return data.
Context file:
const getPets = async () => {
await axios.get('http://localhost:1337/api/pets?populate=*')
.then((res) => {
return res.data
})
.catch(err => {
console.log(err)
})}
Component file:
const [pets, setPets] = useState([])
useEffect( () => {
setPets(getPets())},[])
return (console.log(pets))
The return value is undefined and i don't know why.
Can we help me please?
Tks!
Modify getPets():
const getPets = async () => {
const res = await axios.get('http://localhost:1337/api/pets? populate=*');
return res.data;
}
getPets() returns a promise
useEffect(() => {
getPets().then(res => setPets(res));
}, []);
return (
<>
{pets?.map(pet => { /* some JSX */})}
</>
);

How to prevent object undefined in React

I have react app requeting a flask server, I can return the objects but when I assing the state to a new variable it log undefined, even though I am able to log it
const [characters, setCharacters] = useState([]);
useEffect(() => {
const getData = async () => {
await fetch("http://127.0.0.1:6789/api/load_img_data")
.then((res) => res.json())
.then(
(result) => {
const arrayOfObj = Object.entries(result.imgData).map((e) => e[1]);
setCharacters(arrayOfObj);
},
(error) => {}
);
};
getData();
}, []);
console.log(characters); ## it works fine and log the object on the console
const columnsFromBackend = {
["1"]: {
name: "Terminator Group",
items: characters, ## doesn't work when I map over it as it will be always empty
}
}
so my question what it the best way to assign a state to variable? thanks
You can declare your columnsFromBacked and initialize it as empty object.After you data from api is stored in the hook, then you can assign the appropriate values to columnsFromBacked
Solution 1
let columnsFromBackend= {}
const [characters, setCharacters] = useState([]);
useEffect(() => {
const getData = async () => {
await fetch("http://127.0.0.1:6789/api/load_img_data")
.then((res) => res.json())
.then(
(result) => {
const arrayOfObj = Object.entries(result.imgData).map((e) => e[1]);
setCharacters(arrayOfObj);
columnsFromBackend = {
["1"]: {
name: "Terminator Group",
items: characters
}
}
},
(error) => {}
);
};
getData();
}, []);
}
Solution 2
You can implement useEffect hook with dependency on character hook.
sample code
useEffect(()=>{
columnsFromBackend = {...} //your code
}, [character]);

React useEffect gets data from database but not in time to be used in the component

I am using useEffect to get data from an api.
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const cats = await response.json();
console.log(cats);
} catch (e) {
console.error(e);
}
};
fetchData();
}, []);
The problem is when I try to use it in the return, its value is undefined.
{cats.map((data) => {
cats has value when I console.log it.
I cannot use componentDidMount because all my code is functional components.
Edit: I updated the code as per answers below but still get
TypeError: cats.map is not a function
All answers below actually make sense but I am not sure why its not working.
export default function Posts() {
const [cats, setCats] = useState();
useEffect(() => {
fetch(`/api/posts/getCats`)
.then(res => res.json())
.then(setCats)
.catch(console.error);
}, []);
return (
<div>
{cats?.map((data) => {
<h4>{data.main}</h4>
})}
</div>
)
}
This is because React renders your screen before finishing to get response from API. When you render screen, variable cats doesn't have values. You can run useEffect after each rendering. You can rerender by changing state from useEffect (This technique is often used). Do not forget to add [] or [cats] as a dependency of useEffect (second params) otherwise you will get infinite loop.
Below code works even when cats === [] or some array.
export default () => {
const [cats, setCats] = useState([])
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const result = await response.json();
setCats(result)
} catch (e) {
}
};
fetchData();
}, []);
return (
<div>
{cats.map(cat => <div>cat</div>)}
</div>)
}
You have to map the cats data into state.
const [cats, setCats] = useState([]);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const data = await response.json();
setCats(data);
} catch (e) {
console.error(e);
}
};
fetchData();
}, []);
You need to
call setCats when the response comes back (right now, you're just logging it)
.map only once cats has been populated:
const [cats, setCats] = useState();
useEffect(() => {
fetch(`/api/posts/getCats`)
.then(res => res.json())
.then(result => setCats(result.cats))
.catch(console.error);
}, []);
return (
<div>
{cats?.map((data) => {
// ...

React hooks - fetching data from api and passing to a component

So basically, I'm trying to fetch data from api and pass it to Component.
I create usePosition hook to get my positon from browser, and then get response from api. I really don't know how to wait with useEffect for my position, when i'm executing this code now I'm getting always log 'no position'.
const usePosition = () => {
const [error, setError] = useState(null);
const [position, setPosition] = useState();
useEffect(() => {
const geo = navigator.geolocation;
if(!geo) {
setError('Geolocation is not supported.');
return;
}
const handleSuccess = position => {
const { latitude, longitude } = position.coords;
setPosition({
latitude,
longitude
});
};
const handleError = error => {
setError(error.message);
};
geo.getCurrentPosition(handleSuccess, handleError);
}, []);
return { position, error };
}
function App() {
const {position, error} = usePositon();
const [weather, setWeather] = useState([]);
useEffect(() => {
if(position) {
const URL = `https://api.openweathermap.org/data/2.5/onecall?lat=${position.latitude}&lon=${position.longitude}&exclude=current,minutely,daily&units=metric&lang=pl&appid=${API_KEY}`;
const fetchData = async () => {
const result = await fetch(URL)
.then(res => res.json())
.then(data => data);
setWeather(result.hourly);
}
fetchData();
} else {
console.log('no position');
}
}, []);
return (
<div className="App">
<div>
<Swiper weather={weather}/>
</div>
</div>
)
}
It's all because of [] empty dependencies list down in App's useEffect. It runs exactly once on mount, when usePosition has not requested anything yet. And once it successes later and returns different { error, position } App does not react.
How to solve? Provide things as dependencies:
useEffect(() => {
if(position) {
const URL = `https://api.openweathermap.org/data/2.5/onecall?lat=${position.latitude}&lon=${position.longitude}&exclude=current,minutely,daily&units=metric&lang=pl&appid=${API_KEY}`;
const fetchData = async () => {
const result = await fetch(URL)
.then(res => res.json())
.then(data => data);
setWeather(result.hourly);
}
fetchData();
} else {
console.log('no position');
}
}, [position, error]);

How to translate componentDidMound and componentWillUnmount to UseEffect (React Hooks) with Firebase/Firestore?

How would one go about using the useEffect hook to replace both componentDidMount and componentWillUnmount while working with Firebase? I can't find a solution to this 'unsubscribe' function.
unsubscribe = null;
componentDidMount = async () => {
this.unsubscribe = firestore.collection('posts').onSnapshot(snapshot => {
const posts = snapshot.docs.map(...)
this.setState({ posts })
})
}
componentWillUnmount = () => {
this.unsubscribe()
}
Here's what I tried:
useEffect(() => {
async function getSnapshot() {
const unsubscribe = firestore.collection('posts').onSnapshot(snapshot => {
const posts = snapshot.docs.map(...)
setPosts(posts)
}
getSnapshot()
//return something to clear it? I don't have access to 'unsubscribe'
}, [])
You are actually pretty close with your answer. You weren't using await in your function, so there was no point in using it.
useEffect(() => {
const unsubscribe = firestore.collection('posts').onSnapshot((snapshot) => {
const posts = snapshot.docs.map(...)
setPosts(posts);
});
return () => {
unsubscribe();
};
}, []);
If you did need to use async, you can just utilize the closure to get unsubscribe out of the async function.
useEffect(() => {
let unsubscribe;
async function getSnapshot() {
unsubscribe = firestore.collection('posts').onSnapshot((snapshot) => {
const posts = snapshot.docs.map(...)
setPosts(posts);
});
}
getSnapshot();
return () => {
unsubscribe();
};
}, []);
you're probably going to run into trouble using async inside useEffect, check out https://www.npmjs.com/package/use-async-effect
useAsyncEffect( async() => {
const unsubscribe = await firestore.collection('posts').onSnapshot(snapshot => {
const posts = snapshot.docs.map(...)
setPosts(posts)
}
return () => {
console.log("unmount")
unsubscribe()
};
}, [])
EDIT: actually it seems from the docs that you don't need async at all there:
have you tried this format?
useEffect(
() => {
const unsubscribe = firebase
.firestore()
.collection('recipes')
.doc(id)
.collection('ingredients')
.onSnapshot( snapshot => { const ingredients = [] snapshot.forEach(doc => { ingredients.push(doc) }) setLoading(false) setIngredients(ingredients) }, err => { setError(err) } )
return () => unsubscribe()
},
[id]
)

Resources