I have this ponent that fetches data from a site, and tries to get its keys.
The problem is that it works fine at the first time, but when I refresh the page / save the project in VSCode (and the project refreshes automatically) it shows this error message:
TypeError: Cannot convert undefined or null to object
14 | )
15 |
16 | return (
> 17 | <div className="rates">
| ^ 18 | {Object.keys(rates.rates).map(
19 | rate => <Button variant="outlined">
20 | {rate}
The code (the specific ponent):
import React, { useState, useEffect } from 'react'
import Button from '@mui/material/Button';
const RatesCard = () => {
const [rates, setRates] = useState([]);
useEffect(
()=>{
fetch(";)
.then(ratesResponse => ratesResponse.json())
.then(rates => setRates(rates));
}
,[])
return (
<div className="rates">
{Object.keys(rates.rates).map(
rate => <Button variant="outlined">
{rate}
</Button>
)}
</div>
)
}
export default RatesCard
I have no idea why it works the first time and then it doesn't. My guess is that the useEffect
hook runs only once because of the []
dependency, and then when we refresh it says "Nothing changed, so I won't run the function" Thus we don't fetch anything, and thus rates
is undefined
...
I have no clue why it does this, I can only guess. I will appreciate your kind help! Thank you!
I have this ponent that fetches data from a site, and tries to get its keys.
The problem is that it works fine at the first time, but when I refresh the page / save the project in VSCode (and the project refreshes automatically) it shows this error message:
TypeError: Cannot convert undefined or null to object
14 | )
15 |
16 | return (
> 17 | <div className="rates">
| ^ 18 | {Object.keys(rates.rates).map(
19 | rate => <Button variant="outlined">
20 | {rate}
The code (the specific ponent):
import React, { useState, useEffect } from 'react'
import Button from '@mui/material/Button';
const RatesCard = () => {
const [rates, setRates] = useState([]);
useEffect(
()=>{
fetch("https://api.vatply./rates")
.then(ratesResponse => ratesResponse.json())
.then(rates => setRates(rates));
}
,[])
return (
<div className="rates">
{Object.keys(rates.rates).map(
rate => <Button variant="outlined">
{rate}
</Button>
)}
</div>
)
}
export default RatesCard
I have no idea why it works the first time and then it doesn't. My guess is that the useEffect
hook runs only once because of the []
dependency, and then when we refresh it says "Nothing changed, so I won't run the function" Thus we don't fetch anything, and thus rates
is undefined
...
I have no clue why it does this, I can only guess. I will appreciate your kind help! Thank you!
Share Improve this question asked Oct 15, 2021 at 13:52 0Interest0Interest 1,8425 gold badges21 silver badges41 bronze badges 4-
Your default value for rates should be an empty object, not empty array. Change your state definition to the following:
const [rates, setRates] = useState({rates:[]});
– Suthan Bala Commented Oct 15, 2021 at 13:56 - 1 @SuthanBala Thank you it works! I would also like an explanation about why it happens, what went wrong because I am new to reactjs. Many thanks sir! – 0Interest Commented Oct 15, 2021 at 13:58
- Updated my answer with the explanation. Let me know if you need more explanation on anything.. – Suthan Bala Commented Oct 15, 2021 at 14:08
-
You should use
.then(({ rates }) => setRates(rates));
andObject.keys(rates).map
, that way the intial state[]
forrates
is fine. Wrapping the array in an otherwise empty object is pointless. – user5734311 Commented Oct 15, 2021 at 14:13
2 Answers
Reset to default 3useEffect
runs after your ponent is mounted
which means rates
is just an empty array that doesn't have a property called rates
Object.keys(rates.rates)
try to use
const [rates, setRates] = useState({rates:[]});
or make a loading indicator for example
const RatesCard = () => {
const [rates, setRates] = useState([]);
useEffect(
()=>{
fetch("https://api.vatply./rates")
.then(ratesResponse => ratesResponse.json())
.then(rates => setRates(rates));
}
,[])
if(rates.length === 0) {
return <h1> loading </hi>
}
return (
<div className="rates">
{Object.keys(rates.rates).map(
rate => <Button variant="outlined">
{rate}
</Button>
)}
</div>
)
Your default value for rates should be an empty object with rates
property set to an empty array ({rates: []}
).
By saying the following Object.keys(rates.rates).map
you're going with the assumption that the rates
will be an object, and rates.rates
will be an array.
It works the "first" time due to how hot reloading works, because when you save your files, it injects codes to the browser and not remounting the entire app. Thus, your app will continue from where it left off before, even if it crashed on the previous load..
Change your state definition to the following: const [rates, setRates] = useState({rates:[]});
This will ensure when your app tries to run Object.keys(rates.rates).map
it doesn't crash because you have an array to iterate not null
/undefined