基于滚动速度控制导航栏可见性的React Hook优化教程

基于滚动速度控制导航栏可见性的React Hook优化教程

本文将介绍如何创建一个React Hook,用于根据滚动位置、滚动方向和滚动速度来控制导航栏的可见性,实现类似Humboldt forum网站的交互效果。我们将详细分析原始Hook的实现,并提供优化后的版本,重点关注性能提升,包括使用useCallback进行函数记忆,以及依赖项的合理设置,确保Hook在必要时才更新。

创建基于滚动速度的导航栏可见性控制Hook

在Web开发中,根据用户的滚动行为动态调整导航栏的可见性是一种常见的交互模式,能够提升用户体验。本教程将引导你创建一个React Hook,实现以下功能:

  1. 导航栏初始时固定在页面顶部。
  2. 当用户向下滚动时,隐藏导航栏。
  3. 当用户向上快速滚动时,重新显示导航栏。
  4. 导航栏始终在页面顶部可见。

原始Hook的实现与分析

以下是原始Hook的代码:

const useScrollspeed = (navHeight: number, maxScrollSpeed: number) => {    const [lastScrollTime, setLastScrollTime] = useState(new date().getTime());   const [currentScrollPos, setCurrentScrollPos] = useState(0)   const [prevScrollPos, setPrevScrollPos] = useState(0);   const [visible, setVisible] = useState(true);    const handleScroll = () => {     const distance = Math.abs(currentScrollPos - prevScrollPos);     const scrollSpeed = distance / (new Date().getTime() - lastScrollTime);         if (currentScrollPos > prevScrollPos) {         setVisible(false);       } else {         if (scrollSpeed > maxScrollSpeed) {           setVisible(true);         }       }      setPrevScrollPos(currentScrollPos);     setLastScrollTime(new Date().getTime());   };    if (typeof window !== "undefined") {     useEffect(() => {       setCurrentScrollPos(window.scrollY)        if (currentScrollPos < navHeight) {         setVisible(true);       }        window.addEventListener("scroll", handleScroll);       return () => window.removeEventListener("scroll", handleScroll);     }, [window.scrollY]);   }     return visible; }; export default useScrollSpeed;

这段代码存在一些潜在的性能问题:

  • handleScroll函数在每次滚动事件触发时都会重新创建,导致不必要的函数重新渲染。
  • useEffect的依赖项window.scrollY会导致每次滚动都会触发useEffect,这可能会导致性能问题。

优化后的Hook实现

为了解决上述问题,我们可以使用useCallback来记忆handleScroll函数,并优化useEffect的依赖项。

import { useState, useEffect, useCallback } from 'react';  const useScrollSpeed = (navHeight: number, maxScrollSpeed: number) => {   const [lastScrollTime, setLastScrollTime] = useState(new Date().getTime());   const [currentScrollPos, setCurrentScrollPos] = useState(0);   const [prevScrollPos, setPrevScrollPos] = useState(0);   const [visible, setVisible] = useState(true);    const handleScroll = useCallback(() => {     const now = new Date().getTime();     const distance = Math.abs(currentScrollPos - prevScrollPos);     const scrollSpeed = distance / (now - lastScrollTime);      if (currentScrollPos > prevScrollPos) {       setVisible(false);     } else {       if (scrollSpeed > maxScrollSpeed) {         setVisible(true);       }     }      setPrevScrollPos(currentScrollPos);     setLastScrollTime(now);   }, [currentScrollPos, prevScrollPos, lastScrollTime, setVisible, maxScrollSpeed]);    useEffect(() => {     const handleInitialScroll = () => {       setCurrentScrollPos(window.scrollY);       if (window.scrollY < navHeight) {         setVisible(true);       }     };      handleInitialScroll(); // 初始化滚动位置和可见性      window.addEventListener('scroll', handleScroll);     return () => window.removeEventListener('scroll', handleScroll);   }, [handleScroll, navHeight]);    return visible; };  export default useScrollSpeed;

优化说明:

  1. useCallback: 使用 useCallback 包裹 handleScroll 函数。这确保了只有当依赖项(currentScrollPos,prevScrollPos,lastScrollTime,setVisible,maxScrollSpeed)发生变化时,handleScroll 函数才会被重新创建。
  2. useEffect依赖项优化: 移除了window.scrollY作为useEffect的依赖项。取而代之的是,在useEffect内部添加了一个handleInitialScroll函数来初始化滚动位置和可见性,并只在组件挂载时执行一次。
  3. 代码可读性: 使用 now 变量存储当前时间,避免多次调用 new Date().getTime(),提高代码可读性

使用示例

import useScrollSpeed from './useScrollSpeed'; import React, { useState, useEffect } from 'react';  const Navbar = () => {   const navHeight = 60; // 导航栏高度   const maxScrollSpeed = 0.5; // 最大滚动速度,用于判断是否快速向上滚动   const isVisible = useScrollSpeed(navHeight, maxScrollSpeed);   const [style, setStyle] = useState({     top: 0,     transition: 'top 0.3s',     position: 'fixed',     width: '100%',     backgroundColor: 'white',     zIndex: 1000   });    useEffect(() => {     setStyle(prevStyle => ({       ...prevStyle,       top: isVisible ? 0 : `-${navHeight}px`     }));   }, [isVisible, navHeight]);     return (     <nav style={style}>       {/* 导航栏内容 */}       <h1>My Website</h1>     </nav>   ); };  export default Navbar;

在这个例子中,Navbar 组件使用 useScrollSpeed Hook 来判断导航栏是否应该可见。根据 isVisible 的值,动态地设置导航栏的 top 样式,实现显示和隐藏的动画效果。

注意事项

  • navHeight 和 maxScrollSpeed 的值需要根据实际情况进行调整。
  • 在复杂的应用中,可以考虑使用节流或防抖技术来进一步优化滚动事件的处理。
  • 确保在组件卸载时移除事件监听器,避免内存泄漏。

总结

通过使用 useCallback 和优化 useEffect 的依赖项,我们可以显著提升基于滚动速度控制导航栏可见性的React Hook的性能。优化后的Hook减少了不必要的函数重新创建和组件重新渲染,从而提高了应用的整体性能和用户体验。希望本教程能够帮助你更好地理解和应用React Hook。

© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享