You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中滚动时为组件添加/移除active类的实现方法

React实现滚动时侧边栏菜单项高亮

核心思路

放弃直接操作DOM的原生JS逻辑,改用React的状态管理+props传递控制侧边栏的active类,同时通过useEffect监听滚动事件,计算当前可视区域对应的分类ID。

步骤1:在父组件中维护激活状态并监听滚动

假设<SideMenu/>和<mainContainer/>都在同一个父组件(比如CategoryPage)中,这里统一处理滚动逻辑和状态:

import { useState, useEffect, useRef } from 'react';
import SideMenu from './SideMenu';
import MainContainer from './MainContainer';

const CategoryPage = () => {
  const [activeCategory, setActiveCategory] = useState('category-1');
  // 缓存分类section的DOM引用,避免每次滚动重复查询DOM
  const categorySectionsRef = useRef([]);

  // 滚动事件处理函数
  const handleScroll = () => {
    categorySectionsRef.current.forEach(section => {
      if (!section) return;
      const rect = section.getBoundingClientRect();
      // 当section顶部距离视窗顶部小于75px且未完全移出视窗时,更新激活分类
      if (rect.top < 75 && rect.bottom > 0) {
        setActiveCategory(section.id);
      }
    });
  };

  useEffect(() => {
    window.addEventListener('scroll', handleScroll, true);
    // 组件卸载时移除监听,避免内存泄漏
    return () => {
      window.removeEventListener('scroll', handleScroll, true);
    };
  }, []);

  return (
    <div className="row">
      <div className="col-lg-3 pr-0">
        <SideMenu activeCategory={activeCategory} />
      </div>
      <div className="col-lg-6 category-products-section">
        {/* 给每个category-section添加ref,存入数组 */}
        <div 
          className="category-section" 
          id="category-1"
          ref={el => categorySectionsRef.current[0] = el}
        >
          {/* 原有内容 */}
        </div>
        <div 
          className="category-section" 
          id="category-2"
          ref={el => categorySectionsRef.current[1] = el}
        >
          {/* 原有内容 */}
        </div>
        {/* 更多分类section... */}
      </div>
    </div>
  );
};

export default CategoryPage;

步骤2:修改SideMenu组件,根据props控制active类

假设<SideMenu/>包含多个菜单项,每个项的name对应分类section的id:

const SideMenu = ({ activeCategory }) => {
  // 菜单项数据可根据实际情况定义,这里示例为数组
  const menuItems = [
    { name: 'category-1', label: 'Festive Offer Best Ever' },
    { name: 'category-2', label: 'Another Category' },
    // 更多菜单项...
  ];

  return (
    <div className="categories-sidebar">
      {menuItems.map(item => (
        <div 
          key={item.name}
          className={`categories-sidebar-item ${item.name === activeCategory ? 'active' : ''}`}
          name={item.name}
        >
          {item.label}
        </div>
      ))}
    </div>
  );
};

export default SideMenu;

关键说明

  1. 避免直接操作DOM:React虚拟DOM会管理DOM更新,直接用document.querySelector修改类名可能导致状态不一致,用useRef缓存DOM节点是更安全的方式。
  2. 状态提升:激活状态需要在两个组件间共享,因此将状态提升到共同父组件,再通过props传递给SideMenu。
  3. 清理监听:在useEffect返回函数中移除滚动监听,防止组件卸载后仍执行回调造成内存泄漏。
  4. 性能优化:如果分类数量较多,可使用节流函数(如lodash.throttle)包装handleScroll,减少滚动时的函数调用次数。

内容的提问来源于stack exchange,提问作者Krish Varma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 12:36:17