Комментарии (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"