React 钩子:即使状态没有改变,也会重新渲染



我正在尝试实现一个简单的标头组件,其中标头在到达某个滚动位置后会隐藏。

我想出了以下解决方案,并认为只有当headerState的值实际更改时,才会重新渲染Header组件,而事实并非如此。组件将在每次更改滚动位置时重新渲染,即使甚至没有调用setHeaderState也是如此。

我在这里错过了什么?它是否因为使用useWindowScroll而重新渲染,我可以以某种方式避免它吗?

import { useWindowScroll } from "react-use";
const useHeaderState = () => {
const [ headerState, setHeaderState ] = React.useState(0);
const { y } = useWindowScroll();
React.useEffect(() => {
if (y > 50 && headerState !== 1) {
setHeaderState(1);
}
}, [y, headerState]);
return headerState;
}
const Header: React.FC = () => {
const headerState = useHeaderState();
console.log("rerender");
return (
<header
className={cn(
"fixed top-0 left-0 w-full text-white z-30 transition-transform transition-250",
headerState === 1 && "-translate-16",
)}
>
<div className="bg-gray-900 h-16 md:h-32">header</div>
</header>
)
};

重新渲染确实是因为useWindowScroll。 如源代码所示,每次窗口滚动时,useWindowScroll调用 setState。

为了避免浪费渲染,您可以自己收听窗口滚动,而不是使用useWindowScroll,例如:

const useHeaderState = () => {
const [headerState, setHeaderState] = React.useState(0);
const frame = useRef(0);
useEffect(() => {
const handler = () => {
cancelAnimationFrame(frame.current);
frame.current = requestAnimationFrame(() => {
if (window.pageYOffset > 50 && headerState !== 1) {
setHeaderState(1);
}
});
};
window.addEventListener("scroll", handler, {
capture: false,
passive: true
});
return () => {
cancelAnimationFrame(frame.current);
window.removeEventListener("scroll", handler);
};
});
return headerState;
};

就像你说的问题是,内部useWindowScroll可能会在自己的useState钩子上调用setState

通常,组件重新渲染不应该是一个问题吗?这有问题吗?

但是,一个选项是将useHeaderState钩移动到父钩子,然后使用React.memo来记忆Header,并且仅在道具更改时才重新渲染。

import * as React from "react";
import { render } from "react-dom";
import { useWindowScroll } from "react-use";
const useHeaderState = () => {
const [headerState, setHeaderState] = React.useState(0);
const { y } = useWindowScroll();
React.useEffect(() => {
if (y > 50 && headerState !== 1) {
setHeaderState(1);
}
}, [y, headerState]);
return headerState;
};
const Header: React.FC<{ headerState: number }> = React.memo(props => {
console.log("rerender");
return (
<header className={props.headerState === 1 && "class"}>
<div className="bg-gray-900 h-16 md:h-32">header</div>
</header>
);
});
function App() {
const headerState = useHeaderState();
return (
<div style={{ height: 2000 }}>
<Header headerState={headerState} />
</div>
);
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);

相关内容

  • 没有找到相关文章

最新更新