Performance comparison between react hooks and react class
I can't understand this behavior why rendering is called twice, and FixedHeader is false even when scroll is >= 30.
I guess it is because of closure, the function remember FixedHeader value, which it had in a moment, function was created... you can create some object outside component and write FixedHeader to its property if you really need to see value inside your scroll event callback
const holder = {}
const Header = props => {
const [FixedHeader, setFixedHeader] = useState('false');
holder.value = FixedHeader
const handleScroll = function () {
console.log(window.scrollY)
console.log(holder.value)
if (window.scrollY >= 30) {
setFixedHeader('true');
} else {
setFixedHeader('false');
}
}
useEffect(() => {
document.addEventListener("scroll", handleScroll)
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return (
but because of react does not rerender immidiately sometimes value will not be right
The reason "rendering" is displayed twice is because you are using different conditions.
For the class component you use:
if (window.scrollY >= 25 && !this.state.fixed) {
// ...
} else if (window.scrollY < 25 && this.state.fixed) {
// ...
}
While the function component uses:
if (window.scrollY >= 30) {
// ...
} else {
// ...
}
To fix this issue you need to add the check the current state.
However like you've already noticed checking FixedHeader
value will always result in the same value (it is not getting updated). So we need to tackle that problem first.
The problem is that setFixedHeader
doesn't update the FixedHeader
in the current context. It tells React to re-render using the passed value as the new FixedHeader
on the next Header
call, but FixedHeader
in the current context is never changed.
useEffect
allows you to return a function that handles clean-up. This function runs if the component unmounts, or before the next call of useEffect
(when the dependency list has changed). Adding FixedHeader
to the dependency list will remove the previous scroll event handler (using the returned clean-up function) and adds a new scroll event handler using the new FixedHeader
value when the FixedHeader
value changes.
const {useEffect, useState} = React;
const Header = props => {
console.log("rendering");
const [fixed, setFixed] = useState(false);
useEffect(() => {
const handleScroll = () => {
console.log(window.scrollY);
console.log(fixed);
if (window.scrollY >= 30 && !fixed) {
setFixed(true);
} else if (window.scrollY < 30 && fixed) {
setFixed(false);
}
};
document.addEventListener("scroll", handleScroll);
return () => document.removeEventListener("scroll", handleScroll);
}, [fixed]);
return null;
}
ReactDOM.render(<Header />, document.querySelector("#header-container"));
body { height: 1000px; }
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="header-container"></div>