useRef для доступа к DOM элементам

useRef — это хук в Preact, предназначенный для хранения изменяемого значения, которое не вызывает повторного рендеринга при изменении. Основное его применение — работа с DOM элементами напрямую, когда требуется получить доступ к их свойствам или методам.

Создание и инициализация ref

Для создания ссылки на DOM элемент используется хук useRef:

import { h } from 'preact';
import { useRef, useEffect } from 'preact/hooks';

function InputFocus() {
    const inputRef = useRef(null);

    useEffect(() => {
        if (inputRef.current) {
            inputRef.current.focus();
        }
    }, []);

    return <input ref={inputRef} type="text" />;
}

В этом примере inputRef хранит ссылку на <input>. После монтирования компонента через useEffect выполняется доступ к свойству current, которое указывает на DOM элемент, и вызывается метод focus().

Ключевой момент: useRef возвращает объект с единственным свойством current. Ссылка сохраняется между рендерами, что делает её идеальной для работы с DOM напрямую.

Отличие от state

В отличие от состояния (useState), изменение значения current не инициирует повторный рендер. Это позволяет безопасно хранить объекты DOM или любые изменяемые данные без влияния на визуальное обновление компонента.

function Timer() {
    const intervalRef = useRef(null);
    const [count, setCount] = useState(0);

    useEffect(() => {
        intervalRef.current = setInterval(() => {
            setCount(c => c + 1);
        }, 1000);

        return () => clearInterval(intervalRef.current);
    }, []);

    return <div>{count}</div>;
}

Здесь intervalRef используется для хранения идентификатора таймера. Это демонстрирует, как useRef подходит для хранения объектов, которые должны быть доступны между рендерами, но не влияют на UI напрямую.

Доступ к DOM элементам

С помощью ref можно:

  • управлять фокусом элементов;
  • измерять размеры и положение DOM узлов (offsetWidth, getBoundingClientRect());
  • взаимодействовать с методами элементов (scrollIntoView(), play(), pause() для медиаконтента).

Пример измерения размеров элемента:

function BoxSize() {
    const boxRef = useRef(null);
    const [size, setSize] = useState({ width: 0, height: 0 });

    useEffect(() => {
        if (boxRef.current) {
            const { offsetWidth, offsetHeight } = boxRef.current;
            setSize({ width: offsetWidth, height: offsetHeight });
        }
    }, []);

    return (
        <div>
            <div ref={boxRef} style={{ width: 200, height: 100, background: 'lightblue' }}></div>
            <p>Ширина: {size.width}px, Высота: {size.height}px</p>
        </div>
    );
}

В этом примере через boxRef.current получаем размеры блока и сохраняем их в состоянии для отображения.

Доступ к дочерним компонентам

useRef может быть использован для доступа не только к обычным DOM элементам, но и к компонентам Preact, если они реализованы с помощью forwardRef. Это открывает возможности вызова методов дочерних компонентов.

import { forwardRef, useImperativeHandle } from 'preact/hooks';

const Child = forwardRef((props, ref) => {
    useImperativeHandle(ref, () => ({
        sayHello: () => console.log('Привет из дочернего компонента')
    }));

    return <div>Дочерний компонент</div>;
});

function Parent() {
    const childRef = useRef();

    return (
        <div>
            <Child ref={childRef} />
            <button onCl ick={() => childRef.current.sayHello()}>Вызвать метод</button>
        </div>
    );
}

Важно: useImperativeHandle позволяет контролировать, какие методы и свойства будут доступны через ref, скрывая внутреннюю реализацию компонента.

Практические рекомендации

  • Использовать ref для работы с DOM, когда необходим прямой доступ к элементу или его методам.
  • Не использовать ref для хранения состояния, которое влияет на рендер. Для этого предназначен useState.
  • При работе с динамически создаваемыми элементами можно использовать массив refs для управления списком элементов.
function List() {
    const itemsRef = useRef([]);

    const scrollToItem = index => {
        itemsRef.current[index]?.scrollIntoView({ beh * avior: 'smooth' });
    };

    return (
        <div>
            {['A', 'B', 'C'].map((item, index) => (
                <div
                    key={index}
                    ref={el => (itemsRef.current[index] = el)}
                    style={{ padding: '10px', border: '1px solid black', margin: '5px' }}
                >
                    {item}
                </div>
            ))}
            <button onCl ick={() => scrollToItem(2)}>Прокрутить к C</button>
        </div>
    );
}

Такой подход удобен для работы со списками элементов и управления их поведением напрямую через DOM.

useRef является мощным инструментом Preact для управления DOM и сохранения изменяемых объектов между рендерами, обеспечивая гибкий контроль над элементами интерфейса без лишних повторных отрисовок.