Рефы хранят значения, которые не используются для рендеринга. В отличие от состояния, изменение рефа не вызывает повторный рендер. Чтение или запись ref.current во время рендера ломает ожидания React. К моменту чтения реф может быть ещё не инициализирован, а его значение — устаревшим или несогласованным.
Линт применяет эти правила только к значениям, которые он считает рефами. Значение считается рефом, если компилятор видит любой из следующих паттернов:
constscrollRef=useRef(null);```- Идентификатор с именем `ref` или с суффиксом `Ref`, который читает или записывает `.current`.```jsbuttonRef.current=node;```- Передан через JSX-проп `ref` (например, ``).```jsx<inputref={inputRef}/>```Когда что-то помечено как реф, этот вывод следует за значением через присваивания, деструктуризацию и вызовы вспомогательных функций. Поэтому линт находит нарушения, даже если `ref.current` читают внутри другой функции, которой реф передали аргументом.## Типичные нарушения {#common-violations}- Чтение `ref.current` во время рендера- Обновление `refs` во время рендера- Использование `refs` для значений, которые должны быть состоянием### Неверно {#invalid}Примеры неверного кода для этого правила:```js// ❌ Reading ref during renderfunctionComponent(){constref=useRef(0);constvalue=ref.current;// Don't read during renderreturn<div>{value}</div>;}// ❌ Modifying ref during renderfunctionComponent({value}){constref=useRef(null);ref.current=value;// Don't modify during renderreturn<div/>;}
// ✅ Read ref in effects/handlersfunctionComponent(){constref=useRef(null);useEffect(()=>{if(ref.current){console.log(ref.current.offsetWidth);// OK in effect}});return<divref={ref}/>;}// ✅ Use state for UI valuesfunctionComponent(){const[count,setCount]=useState(0);return(<buttononClick={()=>setCount(count+1)}>{count}</button>);}// ✅ Lazy initialization of ref valuefunctionComponent(){constref=useRef(null);// Initialize only once on first useif(ref.current===null){ref.current=expensiveComputation();// OK - lazy initialization}consthandleClick=()=>{console.log(ref.current);// Use the initialized value};return<buttononClick={handleClick}>Click</button>;}
Эвристика по имени специально считает ref.current и fooRef.current настоящими рефами. Если вы моделируете собственный объект-контейнер, выберите другое имя (например, box) или перенесите изменяемое значение в состояние. После переименования линт замолкает: компилятор перестаёт считать значение рефом.