The error message gives it away, Cannot read property 'current' of null, the only place where current is called is in weather.current in the src of Card.Img, so we deduce that weather was null during the render.
The reason this happens is because the api call is asynchronus, it doesn't populate the state immediately, so the render happens first and tries to read .current from the initial weather state null.
Solution: in your render method, make sure not to read weather.current while weather is null.
You can for example use {weather && <Card>...</Card} to hide the whole card until the data is loaded and show a loading indicator, or you can use src={weather && weather.current.condition.icon} as a quick workaround.
const Weather = ({capital}) => {
const [weather, setWeather] = useState(null);
useEffect(() => {
console.log("useEffect called");
const getWeather = async () => {
try {
const res = await axios.get(
`http://api.apixu.com/v1/current.json?key=53d601eb03d1412c9c004840192807&q=${capital}`,
);
setWeather(res.data);
} catch (e) {
console.log(e);
}
};
getWeather();
}, [capital]);
console.log(weather);
return (
<Card style={{width: "18rem", marginTop: "25px"}}>
<Card.Img variant="top" src={weather && weather.current.condition.icon} />
<Card.Header style={{textAlign: "center", fontSize: "25px"}}>
Weather in {capital}
</Card.Header>
</Card>
);
};
Answer from Moe on Stack OverflowThe error message gives it away, Cannot read property 'current' of null, the only place where current is called is in weather.current in the src of Card.Img, so we deduce that weather was null during the render.
The reason this happens is because the api call is asynchronus, it doesn't populate the state immediately, so the render happens first and tries to read .current from the initial weather state null.
Solution: in your render method, make sure not to read weather.current while weather is null.
You can for example use {weather && <Card>...</Card} to hide the whole card until the data is loaded and show a loading indicator, or you can use src={weather && weather.current.condition.icon} as a quick workaround.
const Weather = ({capital}) => {
const [weather, setWeather] = useState(null);
useEffect(() => {
console.log("useEffect called");
const getWeather = async () => {
try {
const res = await axios.get(
`http://api.apixu.com/v1/current.json?key=53d601eb03d1412c9c004840192807&q=${capital}`,
);
setWeather(res.data);
} catch (e) {
console.log(e);
}
};
getWeather();
}, [capital]);
console.log(weather);
return (
<Card style={{width: "18rem", marginTop: "25px"}}>
<Card.Img variant="top" src={weather && weather.current.condition.icon} />
<Card.Header style={{textAlign: "center", fontSize: "25px"}}>
Weather in {capital}
</Card.Header>
</Card>
);
};
I had the same puzzling issue one time
You can try adding a key prop on the component when it is created in the parent code
<yourcomponent key="some_unique_value" />
This is because in most cases, when your component is reused, based on the way it is created, it may usually re-render it with some changes instead of creating it again when you reuse it, Hence the useEffect is not going to be called. eg in SwitchRoute, loops, conditionals...
So adding a key will prevent this from happening. If it is in a loop, you need to make sure each element is unique, maybe by including the index i in the key if you can't find any better unique key.
useEffect callback never called
Bug: useEffect not firing when depending on hook value
UseEffect not working.
Bug: useEffect not triggering on [deps] change
Videos
The component adds the event listener once: only on mount. At that time, it adds the handleScroll listener that is "captured" with that specific isHeaderMoved value at that specific point in time. Whenever the page is scrolled, the callback that will be called is the one with that initial isHeaderMoved value. The current code basically calls handleScroll with false over and over.
In short, you need to add isHeaderMoved as a dependency to the effect. You should also move the handleScroll listener inside the effect, so its dependencies can be tracked more properly by the hooks eslint plugin.
useEffect(
() => {
const handleScroll = () => {
if (window.pageYOffset > 0) {
if (!isHeaderMoved) {
setIsHeaderMoved(true)
}
} else if (isHeaderMoved) {
setIsHeaderMoved(false)
}
}
window.addEventListener("scroll", handleScroll)
return () => {
window.removeEventListener("scroll", handleScroll)
}
},
[isHeaderMoved],
)
Refer to these sections from Dan Abromov's posts on the subject (which have examples that can explain it better than I could):
- https://overreacted.io/a-complete-guide-to-useeffect/#each-render-has-its-own-event-handlers
- https://overreacted.io/a-complete-guide-to-useeffect/#each-render-has-its-own-effects
- https://overreacted.io/a-complete-guide-to-useeffect/#moving-functions-inside-effects
It's worth going through the whole post to understand things fully, if you have the time.
From skyboxer's comment, it occurred to me that this would also work, but I'll keep all of the above for informational purposes.
useEffect(() => {
const handleScroll = () => {
setIsHeaderMoved(window.pageYOffset > 0)
}
window.addEventListener("scroll", handleScroll)
return () => {
window.removeEventListener("scroll", handleScroll)
}
}, [])
I had the same annoying problem once
try adding a key prop on the component when it is created in the parent code
<yourcomponent key="some_unique_value" />
This is because in most cases, especially when your component is reused, it may usually re-render only with some changes instead of actually creating it again when you reuse it, based on the way it's created, Hence the useEffect is not going to be called. eg inside Switch, Router, loops, conditionals...
So adding a key will prevent this from happening. If it is in a loop, you need to make sure each element is unique, maybe by including the index i in the key if you can't find any better unique key.
I am new to nextJS and learning it. Can anybody tell me why UseEffect is not working in my components even though I used 'use client' directive.''
Here is the code.
'use client'
import Link from 'next/link';
import { useRouter } from 'next/navigation'
import React, { useEffect } from 'react'
const NotFound = () => {
const router = useRouter();
useEffect(() => {
console.log('useefoefoiewhf ran1');
setTimeout(()=>{
router.push('/')
},3000)
}, [])
return (
<div className='not-found'>
<h1>Ooooops.....</h1>
<h2>That page cannot be found.</h2>
<p>Go back to the <Link href={'/'}>Homepage.</Link>
</p>
</div>
)
}
export default NotFound;
The useEffect hook is guaranteed to run at least once at the end of the initial render.
getData is an async function and the useEffect callback code is not waiting for it to resolve. Easy solution is to chain from the implicitly returned Promise from getData and access the resolved value to update the arrData state. Make sure to remove the state from the useEffect's dependency array so that you don't create a render loop.
The getData implementation could be clean/tightened up by just returning the fetch result, no need to save into a temp variable first.
async function getData() {
return await fetch(".....")
.then((res) => res.json());
}
useEffect(() => {
console.log("if it works, this line should be shown");
getData().then((data) => {
setArrData(data);
});
}, []); // <-- empty dependency so effect called once on mount
Additionally, since arrData is initially undefined, arrData[0] is likely to throw an error. You may want to provide valid initial state, and a fallback value in case the first element is undefined, so you don't attempt to access properties of an undefined object.
const [arrData, setArrData] = useState([]);
...
const data = arrData[0] || {}; // data is at least an object
return (
<GifCompoment
id={data.id}
name={data.name}
activeTimeTo={data.activeTimeTo}
activeTimeFrom={data.activeTimeFrom}
requiredPoints={data.requiredPoints}
imageUrl={data.imageUrl}
/>
);
You should call state setter insede of Promise
function App() {
const [arrData, setArrData] = useState();
function getData() {
fetch("/api/hidden")
.then((res) => res.json())
.then((data) => setArrData(data));
}
useEffect(() => {
console.log("if it works, this line should be shown");
getData();
}, []);
return ...
}
Hello,
I have a functional object which has three variables managed by the useState hook. These are categories, activeCategories and shownGames. For now I need something simple - run a function when categories update. I am using useEffect hook but it doesn't seem to work.
This is the code for the needed parts - https://pastie.io/uyakqz.js
I use JSON.stringify to check for the category updates (found that solution on StackOverflow). The issue is that the function specified in the useEffect hook doesn't run (it only runs when I load my page). Any help is appreciated!