← Назад к вопросам

Как обратиться к DOM из React?

1.7 Middle🔥 231 комментариев
#React

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как обратиться к DOM из React

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

Способ 1: useRef() хук (Functional Components)

useRef() — это встроенный хук React для сохранения ссылки на DOM элемент.

Базовый пример:

import { useRef } from 'react';

function TextInput() {
  const inputRef = useRef(null);
  
  const handleClick = () => {
    // Обращаемся к DOM элементу через .current
    inputRef.current.focus();
    console.log('Значение инпута:', inputRef.current.value);
  };
  
  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>Fokus</button>
    </>
  );
}

Пример с видео элементом:

function VideoPlayer() {
  const videoRef = useRef(null);
  
  const handlePlay = () => {
    videoRef.current.play();
  };
  
  const handlePause = () => {
    videoRef.current.pause();
  };
  
  return (
    <>
      <video ref={videoRef} width="640" height="360">
        <source src="video.mp4" type="video/mp4" />
      </video>
      <button onClick={handlePlay}>Play</button>
      <button onClick={handlePause}>Pause</button>
    </>
  );
}

Способ 2: Обращение к DOM элементам в useEffect()

useEffect() позволяет выполнять побочные эффекты после рендера:

import { useRef, useEffect } from 'react';

function Counter() {
  const countRef = useRef(null);
  const [count, setCount] = React.useState(0);
  
  useEffect(() => {
    // Обновляем текст в DOM
    if (countRef.current) {
      countRef.current.textContent = count;
    }
  }, [count]);
  
  return (
    <>
      <div ref={countRef}>0</div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </>
  );
}

Способ 3: Вычисление размеров и позиций

Измерение элемента:

import { useRef, useEffect, useState } from 'react';

function MeasureElement() {
  const elementRef = useRef(null);
  const [width, setWidth] = useState(0);
  
  useEffect(() => {
    if (elementRef.current) {
      setWidth(elementRef.current.offsetWidth);
    }
  }, []);
  
  return (
    <>
      <div ref={elementRef}>Элемент</div>
      <p>Ширина: {width}px</p>
    </>
  );
}

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

function ResponsiveElement() {
  const containerRef = useRef(null);
  const [width, setWidth] = useState(0);
  
  useEffect(() => {
    const resizeObserver = new ResizeObserver((entries) => {
      setWidth(entries[0].contentRect.width);
    });
    
    if (containerRef.current) {
      resizeObserver.observe(containerRef.current);
    }
    
    return () => resizeObserver.disconnect();
  }, []);
  
  return <div ref={containerRef}>Ширина: {width}px</div>;
}

Способ 4: Управление фокусом

Автоматический фокус на инпут:

function Form() {
  const firstInputRef = useRef(null);
  
  useEffect(() => {
    // Фокусируемся на первый инпут при загрузке
    firstInputRef.current?.focus();
  }, []);
  
  return (
    <form>
      <input ref={firstInputRef} type="text" placeholder="Имя" />
      <input type="email" placeholder="Email" />
    </form>
  );
}

Управление фокусом между элементами:

function FormWithNavigation() {
  const emailRef = useRef(null);
  const passwordRef = useRef(null);
  
  const handleEmailKeyPress = (e) => {
    if (e.key === 'Enter') {
      passwordRef.current?.focus();
    }
  };
  
  return (
    <form>
      <input 
        ref={emailRef} 
        type="email" 
        onKeyPress={handleEmailKeyPress}
      />
      <input ref={passwordRef} type="password" />
    </form>
  );
}

Способ 5: Работа с методами элементов

Управление плеером:

function AudioPlayer() {
  const audioRef = useRef(null);
  const [isPlaying, setIsPlaying] = useState(false);
  
  const togglePlay = () => {
    if (isPlaying) {
      audioRef.current?.pause();
    } else {
      audioRef.current?.play();
    }
    setIsPlaying(!isPlaying);
  };
  
  return (
    <>
      <audio ref={audioRef} src="song.mp3" />
      <button onClick={togglePlay}>
        {isPlaying ? 'Pause' : 'Play'}
      </button>
    </>
  );
}

Способ 6: Получение текущего значения инпута

function SearchForm() {
  const searchRef = useRef(null);
  
  const handleSearch = () => {
    const query = searchRef.current.value;
    console.log('Поиск:', query);
    // Выполняем поиск
  };
  
  return (
    <>
      <input ref={searchRef} type="text" placeholder="Поиск..." />
      <button onClick={handleSearch}>Поиск</button>
    </>
  );
}

Способ 7: Обращение к кастомным компонентам

Использование forwardRef():

import { useRef, forwardRef } from 'react';

const CustomInput = forwardRef((props, ref) => {
  return <input ref={ref} {...props} />;
});

function App() {
  const customInputRef = useRef(null);
  
  const handleFocus = () => {
    customInputRef.current?.focus();
  };
  
  return (
    <>
      <CustomInput ref={customInputRef} />
      <button onClick={handleFocus}>Focus</button>
    </>
  );
}

Использование useImperativeHandle():

import { useRef, useImperativeHandle, forwardRef } from 'react';

const TextInput = forwardRef((props, ref) => {
  const inputRef = useRef(null);
  
  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current?.focus(),
    clear: () => {
      inputRef.current.value = '';
    }
  }));
  
  return <input ref={inputRef} {...props} />;
});

function App() {
  const textInputRef = useRef(null);
  
  return (
    <>
      <TextInput ref={textInputRef} />
      <button onClick={() => textInputRef.current?.focus()}>Focus</button>
      <button onClick={() => textInputRef.current?.clear()}>Clear</button>
    </>
  );
}

Способ 8: Обращение через document.getElementById()

Когда нельзя использовать ref:

function App() {
  useEffect(() => {
    // Получаем элемент из документа
    const footer = document.getElementById('footer');
    if (footer) {
      footer.style.display = 'none';
    }
  }, []);
  
  return <div id="footer">Footer</div>;
}

Способ 9: querySelector и querySelectorAll

function ListManager() {
  const containerRef = useRef(null);
  
  useEffect(() => {
    // Находим все элементы внутри контейнера
    const items = containerRef.current?.querySelectorAll('li');
    if (items) {
      console.log('Найдено элементов:', items.length);
    }
  }, []);
  
  return (
    <ul ref={containerRef}>
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
    </ul>
  );
}

Когда НЕ использовать ref

// ❌ Плохо: используем ref для того, что можно сделать через состояние
const badExample = () => {
  const countRef = useRef(0);
  return <button onClick={() => countRef.current++}>{countRef.current}</button>;
  // Не обновит UI!
};

// ✅ Хорошо: используем состояние
const goodExample = () => {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
};

Выводы

  • useRef() — основной способ обращения к DOM в functional components
  • useEffect() — для выполнения побочных эффектов после рендера
  • forwardRef() и useImperativeHandle() — для передачи ref в кастомные компоненты
  • Избегай обращения к DOM, когда можно использовать состояние и props
  • Ref идеален для управления фокусом, медиа плеерами и значениями инпутов
  • Всегда очищай слушатели и подписки в return функции useEffect