Scrollspy с ReactJS: легкая навигация и управление активным состоянием

Вы создаете приложение ReactJS и хотите реализовать функцию прокрутки? Не смотрите дальше! В этой статье мы рассмотрим различные методы включения функциональности прокрутки в ваш проект ReactJS. Scrollspy позволяет отслеживать положение прокрутки пользователя и соответствующим образом обновлять меню навигации, обеспечивая плавный и интерактивный просмотр. Итак, приступим!

Метод 1: Библиотека React Scrollspy
Один из самых простых способов реализовать прокрутку в ReactJS — использовать специальную библиотеку. Одним из популярных вариантов является библиотека «react-scrollspy». Вы можете установить его с помощью npm или Yarn:

npm install react-scrollspy

После установки импортируйте необходимые компоненты и определите меню навигации с соответствующими идентификаторами разделов. Вот пример:

import React from 'react';
import { ScrollspyNav } from 'react-scrollspy';
const App = () => {
  return (
    <div>
      {/* Your content here */}
      <ScrollspyNav
        scrollTargetIds={['section1', 'section2', 'section3']}
        activeNavClass="active"
      >
        <ul>
          <li><a href="#section1">Section 1</a></li>
          <li><a href="#section2">Section 2</a></li>
          <li><a href="#section3">Section 3</a></li>
        </ul>
      </ScrollspyNav>
    </div>
  );
};
export default App;

Метод 2: собственный хук Scrollspy
Если вы предпочитаете более индивидуальный подход, вы можете создать многоразовый хук прокрутки. Вот пример реализации:

import React, { useEffect, useState } from 'react';
const useScrollSpy = (targetIds) => {
  const [activeSection, setActiveSection] = useState(null);
  useEffect(() => {
    const handleScroll = () => {
      const scrollPosition = window.scrollY;
      const sections = targetIds.map((id) => document.getElementById(id));
      const activeIndex = sections.findIndex(
        (section) =>
          section.offsetTop <= scrollPosition + section.offsetHeight &&
          section.offsetTop + section.offsetHeight > scrollPosition
      );
      setActiveSection(activeIndex >= 0 ? targetIds[activeIndex] : null);
    };
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, [targetIds]);
  return activeSection;
};
const App = () => {
  const activeSection = useScrollSpy(['section1', 'section2', 'section3']);
  return (
    <div>
      {/* Your content here */}
      <ul>
        <li className={activeSection === 'section1' ? 'active' : ''}>
          <a href="#section1">Section 1</a>
        </li>
        <li className={activeSection === 'section2' ? 'active' : ''}>
          <a href="#section2">Section 2</a>
        </li>
        <li className={activeSection === 'section3' ? 'active' : ''}>
          <a href="#section3">Section 3</a>
        </li>
      </ul>
    </div>
  );
};
export default App;

Метод 3: API Intersection Observer
Другой подход — использовать API Intersection Observer, который позволяет эффективно отслеживать изменения видимости элементов. Вот пример реализации:

import React, { useEffect, useRef, useState } from 'react';
const App = () => {
  const [activeSection, setActiveSection] = useState(null);
  const sectionRefs = useRef([]);
  useEffect(() => {
    const handleScroll = () => {
      const scrollPosition = window.scrollY;
      const activeIndex = sectionRefs.current.findIndex(
        (ref) =>
          ref.current.offsetTop <= scrollPosition + ref.current.offsetHeight &&
          ref.current.offsetTop + ref.current.offsetHeight > scrollPosition
      );
      setActiveSection(
        activeIndex >= 0 ? `section${activeIndex + 1}` : null
      );
    };
    const observerOptions = {
      root: null,
      rootMargin: '0px',
      threshold: 0.5,
    };
    const observer = new IntersectionObserver(handleScroll, observerOptions);
    sectionRefs.current.forEach((ref) => {
      if (ref.current) {
        observer.observe(ref.current);
      }
    });
    return () => observer.disconnect();
  }, []);
  return (
    <div>
      {/* Your content here */}
      <ul>
        <li className={activeSection === 'section1' ? 'active' : ''}>
          <a href="#section1">Section 1</a>
        </li>
        <li className={activeSection === 'section2'? 'active' : ''}>
          <a href="#section2">Section 2</a>
        </li>
        <li className={activeSection === 'section3' ? 'active' : ''}>
          <a href="#section3">Section 3</a>
        </li>
      </ul>
      <div ref={sectionRefs.current[0]} id="section1">
        {/* Section 1 content */}
      </div>
      <div ref={sectionRefs.current[1]} id="section2">
        {/* Section 2 content */}
      </div>
      <div ref={sectionRefs.current[2]} id="section3">
        {/* Section 3 content */}
      </div>
    </div>
  );
};
export default App;

В этом методе мы используем хук useRefдля создания изменяемого массива ссылок для каждого раздела. IntersectionObserverнастроен для отслеживания видимости каждого раздела, и активный раздел обновляется соответствующим образом.

Заключение
Внедрение прокрутки в ваше приложение ReactJS повышает удобство работы пользователя, обеспечивая плавную навигацию и выделение активного раздела. В этой статье мы рассмотрели несколько методов достижения функциональности прокрутки, включая использование специальной библиотеки, создание специального перехватчика и использование API Intersection Observer. Выберите метод, который лучше всего соответствует требованиям вашего проекта, и начните создавать более привлекательный пользовательский интерфейс.

Не забудьте протестировать и оптимизировать функцию прокрутки, чтобы убедиться, что она хорошо работает на разных устройствах и в браузерах. Приятной прокрутки!