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

В каких кейсах используешь refs

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

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

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

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

Кейсы для использования refs

Золотое правило: Избегай refs. Используй их только если нет других вариантов.

Вот случаи когда refs действительно нужны:

1. Управление focus

function SearchInput() {
  const inputRef = useRef<HTMLInputElement>(null);
  
  const focusInput = () => {
    inputRef.current?.focus();
  };
  
  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>Focus input</button>
    </>
  );
}

Почему refs? Нет способа через React API управлять focus. Это DOM операция.

Пример из реальной жизни:

  • Автофокус на инпут в модальном окне
  • Фокус при критической ошибке
  • Фокус на поле для заполнения

2. Триггеринг анимаций

function Modal() {
  const modalRef = useRef<HTMLDivElement>(null);
  const [isOpen, setIsOpen] = useState(false);
  
  const openModal = () => {
    setIsOpen(true);
    // Trigger анимация через классы
    modalRef.current?.classList.add('modal-open');
  };
  
  return (
    <div ref={modalRef} className="modal">
      <button onClick={openModal}>Open</button>
    </div>
  );
}

Лучше через state:

function Modal() {
  const [isOpen, setIsOpen] = useState(false);
  
  return (
    <div className={cn('modal', isOpen && 'modal-open')}>
      <button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
    </div>
  );
}

3. Управление audio/video

function AudioPlayer() {
  const audioRef = useRef<HTMLAudioElement>(null);
  
  const play = () => audioRef.current?.play();
  const pause = () => audioRef.current?.pause();
  const setVolume = (vol: number) => {
    if (audioRef.current) audioRef.current.volume = vol;
  };
  
  return (
    <>
      <audio ref={audioRef} src="music.mp3" />
      <button onClick={play}>Play</button>
      <button onClick={pause}>Pause</button>
      <input type="range" min="0" max="1" step="0.1" onChange={(e) => setVolume(parseFloat(e.target.value))} />
    </>
  );
}

Почему refs? Audio API - это DOM API, не React.

4. Интеграция с third-party library

function MapComponent() {
  const mapRef = useRef<HTMLDivElement>(null);
  
  useEffect(() => {
    if (!mapRef.current) return;
    
    // Инициализируем third-party library (например, Leaflet)
    const map = L.map(mapRef.current).setView([51.505, -0.09], 13);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
    
    return () => {
      map.remove(); // Cleanup
    };
  }, []);
  
  return <div ref={mapRef} style={{ height: '400px' }} />;
}

Почему refs? Третья библиотека требует доступ к DOM элементу.

5. Получение размеров и позиций элемента

function TextInput() {
  const inputRef = useRef<HTMLInputElement>(null);
  const [width, setWidth] = useState(0);
  
  useEffect(() => {
    if (!inputRef.current) return;
    
    const observer = new ResizeObserver(() => {
      setWidth(inputRef.current!.offsetWidth);
    });
    
    observer.observe(inputRef.current);
    
    return () => observer.disconnect();
  }, []);
  
  return (
    <>
      <input ref={inputRef} type="text" />
      <p>Width: {width}px</p>
    </>
  );
}

Почему refs? ResizeObserver - это DOM API.

6. Текстовое содержимое (редко)

function CopyButton() {
  const textRef = useRef<HTMLDivElement>(null);
  
  const copyToClipboard = () => {
    const text = textRef.current?.textContent;
    if (text) navigator.clipboard.writeText(text);
  };
  
  return (
    <>
      <div ref={textRef}>Copy this text</div>
      <button onClick={copyToClipboard}>Copy</button>
    </>
  );
}

Лучше через state:

function CopyButton() {
  const [text, setText] = useState('Copy this text');
  
  const copyToClipboard = () => {
    navigator.clipboard.writeText(text);
  };
  
  return (
    <>
      <div>{text}</div>
      <button onClick={copyToClipboard}>Copy</button>
    </>
  );
}

Anti-pattern: НЕ используй refs для этого

// ❌ НЕПРАВИЛЬНО - можно через state
function Counter() {
  const countRef = useRef(0);
  
  const increment = () => {
    countRef.current++; // Не перерендерится!
  };
  
  return <p>{countRef.current}</p>; // Всегда 0!
}

// ✅ ПРАВИЛЬНО
function Counter() {
  const [count, setCount] = useState(0);
  
  const increment = () => {
    setCount(count + 1);
  };
  
  return <p>{count}</p>;
}

// ❌ НЕПРАВИЛЬНО - можно через props/context
function Parent() {
  const childRef = useRef<any>(null);
  
  const callChildMethod = () => {
    childRef.current?.doSomething();
  };
  
  return <Child ref={childRef} />;
}

// ✅ ПРАВИЛЬНО
function Parent() {
  const [action, setAction] = useState<'do' | null>(null);
  
  return <Child action={action} />;
}

useCallback для избежания refs

// С ref (не лучший вариант)
function FormWithRef() {
  const formRef = useRef<HTMLFormElement>(null);
  
  const handleSubmit = () => {
    formRef.current?.submit();
  };
  
  return (
    <form ref={formRef} onSubmit={(e) => e.preventDefault()}>
      <input />
      <button onClick={handleSubmit}>Submit</button>
    </form>
  );
}

// Лучше - через event
function FormWithEvent() {
  const [isSubmitted, setIsSubmitted] = useState(false);
  
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    setIsSubmitted(true);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input />
      <button type="submit">Submit</button>
    </form>
  );
}

useImperativeHandle для контролируемых компонентов

// Компонент с методами через ref
const Input = forwardRef<{ focus: () => void }>((props, ref) => {
  const inputRef = useRef<HTMLInputElement>(null);
  
  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current?.focus()
  }), []);
  
  return <input ref={inputRef} {...props} />;
});

// Использование
function Parent() {
  const inputRef = useRef<{ focus: () => void }>(null);
  
  return (
    <>
      <Input ref={inputRef} />
      <button onClick={() => inputRef.current?.focus()}>Focus</button>
    </>
  );
}

Правило: Refs vs State

// Используй REFS если:
// - Нужен доступ к DOM
// - НЕ хочешь перерендеривать компонент
// - Управляешь focus, play/pause, selection

// Используй STATE если:
// - Нужно вызвать перерендер
// - Это часть UI (видно пользователю)
// - Это может измениться во время жизни компонента
// - Может быть несколько экземпляров значения

Мой совет

// В 90% случаев - НЕ используй refs
// В 10% случаях (DOM API) - используй refs

// Правило: Если ты пишешь ref для управления state/props,
// ты делаешь что-то неправильно

Итоги

  • Refs - для DOM операций (focus, play/pause, animation)
  • State - для всего остального
  • useImperativeHandle - для контроля через ref
  • Избегай refs - они усложняют код
  • useEffect - для интеграции с third-party
  • Золотое правило - "Не используй refs пока не попробуешь state"
В каких кейсах используешь refs | PrepBro