Использование в React

Waypoints — это небольшая и удобная библиотека для отслеживания прокрутки и выполнения функций при достижении определённых точек на странице. В React интеграция Waypoints требует учёта жизненного цикла компонентов, поскольку библиотека работает напрямую с DOM.

Для начала необходимо установить библиотеку через npm или yarn:

npm install waypoints

или

yarn add waypoints

После установки важно помнить, что Waypoints не предоставляет собственные React-компоненты, поэтому используется в сочетании с refs для доступа к DOM-элементам.


Создание Waypoint с использованием React Ref

Основная концепция — назначение рефа на элемент, за которым нужно следить, и создание экземпляра Waypoint в useEffect:

import React, { useRef, useEffect } from 'react';
import Waypoint from 'waypoints/lib/noframework.waypoints';

function ScrollComponent() {
  const sectionRef = useRef(null);

  useEffect(() => {
    const waypoint = new Waypoint({
      element: sectionRef.current,
      handler: function(direction) {
        console.log(`Элемент достигнут, направление: ${direction}`);
      },
      offset: '50%' // активируется, когда элемент окажется посередине окна
    });

    return () => waypoint.destroy();
  }, []);

  return (
    <div style={{ height: '150vh' }}>
      <div ref={sectionRef} style={{ marginTop: '100vh', height: '100px', backgroundColor: '#f0f0f0' }}>
        Следим за этой секцией
      </div>
    </div>
  );
}

export default ScrollComponent;

Ключевые моменты:

  • Использование useRef позволяет Waypoints получить доступ к реальному DOM-элементу.
  • handler вызывается при пересечении точки с учётом направления прокрутки (up или down).
  • offset задаёт смещение с верхней границы окна браузера, при котором срабатывает waypoint.
  • Обязательно вызывать destroy при размонтировании компонента, чтобы избежать утечек памяти.

Использование нескольких Waypoints

Для отслеживания нескольких элементов можно создавать отдельные экземпляры Waypoint для каждого элемента:

import React, { useRef, useEffect } from 'react';
import Waypoint from 'waypoints/lib/noframework.waypoints';

function MultiSection() {
  const sections = [useRef(null), useRef(null), useRef(null)];

  useEffect(() => {
    const waypoints = sections.map((ref, index) =>
      new Waypoint({
        element: ref.current,
        handler: function(direction) {
          console.log(`Секция ${index + 1} достигнута, направление: ${direction}`);
        },
        offset: '75%'
      })
    );

    return () => waypoints.forEach(wp => wp.destroy());
  }, []);

  return (
    <div>
      {sections.map((ref, i) => (
        <div key={i} ref={ref} style={{ height: '100vh', border: '1px solid #ccc', marginBottom: '20px' }}>
          Секция {i + 1}
        </div>
      ))}
    </div>
  );
}

export default MultiSection;

Особенности:

  • Каждый waypoint создаётся отдельно, что позволяет иметь уникальные обработчики для разных элементов.
  • offset можно задавать индивидуально для каждой секции.
  • Для удобства массив refs и waypoints позволяют динамически управлять количеством отслеживаемых элементов.

Интеграция с состоянием React

Чтобы использовать Waypoints совместно с состоянием компонента, необходимо аккуратно обновлять стейт внутри handler:

import React, { useState, useRef, useEffect } from 'react';
import Waypoint from 'waypoints/lib/noframework.waypoints';

function ActiveSectionTracker() {
  const [activeIndex, setActiveIndex] = useState(null);
  const sections = [useRef(null), useRef(null), useRef(null)];

  useEffect(() => {
    const waypoints = sections.map((ref, index) =>
      new Waypoint({
        element: ref.current,
        handler: function(direction) {
          setActiveIndex(index);
        },
        offset: '50%'
      })
    );

    return () => waypoints.forEach(wp => wp.destroy());
  }, []);

  return (
    <div>
      {sections.map((ref, i) => (
        <div
          key={i}
          ref={ref}
          style={{
            height: '100vh',
            border: '1px solid #ccc',
            backgroundColor: activeIndex === i ? '#d0f0d0' : '#f0f0f0'
          }}
        >
          Секция {i + 1}
        </div>
      ))}
    </div>
  );
}

export default ActiveSectionTracker;

Выводы по работе со стейтом:

  • Использование setState внутри handler обновляет компонент и позволяет динамически менять стили или контент.
  • Следует избегать частых переработок стейта для улучшения производительности при прокрутке.
  • В сложных случаях рекомендуется использовать useCallback или мемоизацию для функций обработчиков.

Настройка срабатывания Waypoints по горизонтальной прокрутке

Waypoints поддерживает отслеживание горизонтальной прокрутки через параметр horizontal:

new Waypoint({
  element: elementRef.current,
  handler: function(direction) { console.log(direction); },
  horizontal: true
});

Особенности:

  • horizontal: true переключает отслеживание с вертикальной на горизонтальную ось.
  • Полезно для каруселей или горизонтальных скролл-секций.
  • Параметр offset также работает по горизонтали.

Управление точками срабатывания через offset и context

Waypoints позволяет гибко управлять точками срабатывания:

  • offset — число пикселей или процент окна, при котором срабатывает waypoint.
  • context — элемент, относительно которого происходит отслеживание, по умолчанию это window.

Пример:

const containerRef = useRef(null);
new Waypoint({
  element: sectionRef.current,
  handler: () => console.log('Waypoint сработал внутри контейнера'),
  offset: '30%',
  context: containerRef.current
});

Особенности использования context:

  • Позволяет создавать независимые скролл-зоны.
  • Полезно при вложенных скролл-блоках в React, например, модальные окна или секции с overflow: auto.

Waypoints в React предоставляет гибкие возможности для отслеживания прокрутки и создания динамических интерфейсов. Правильное использование refs, useEffect, управления стейтом и offset обеспечивает стабильную и производительную работу в любых сценариях.