useRef
лишь частично похож на ref
в React (просто структура объекта с единственным полем current
). Хук
useRef
нацелен на сохранение некоторых данных между рендерами и их изменениеданные не вызывают повторную визуализацию (в отличие от useState
).
Также просто мягкое напоминание: лучше избегать инициализации ловушек в циклах или if
.Это первое правило хуков .
Имея это в виду, мы:
- создаем массив и сохраняем его между рендерами на
useRef
- мы инициализируем каждый элемент массива с помощью
createRef()
мы можем ссылаться на список, используя .current
нотацию
const Component = () => {
let refs = useRef([React.createRef(), React.createRef()]);
useEffect(() => {
refs.current[0].current.focus()
}, []);
return (<ul>
{['left', 'right'].map((el, i) =>
<li key={i}><input ref={refs.current[i]} value={el} /></li>
)}
</ul>)
}
Это можно было безопасно изменитьмассив (скажем, путем изменения его длины).Но не забывайте, что изменяющиеся данные, хранящиеся в useRef
, не вызывают повторную визуализацию.Таким образом, чтобы изменить длину для повторного рендеринга, нам нужно задействовать useState
.
const Component = () => {
const [length, setLength] = useState(2);
const refs = useRef([React.createRef(), React.createRef()]);
function updateLength({ target: { value }}) {
setLength(value);
refs.current = refs.current.splice(0, value);
for(let i = 0; i< value; i++) {
refs.current[i] = refs.current[i] || React.createRef();
}
refs.current = refs.current.map((item) => item || React.createRef());
}
useEffect(() => {
refs.current[refs.current.length - 1].current.focus()
}, [length]);
return (<>
<ul>
{refs.current.map((el, i) =>
<li key={i}><input ref={refs.current[i]} value={i} /></li>
)}
</ul>
<input value={refs.current.length} type="number" onChange={updateLength} />
</>)
}
Также не пытайтесь получить доступ к refs.current[0].current
при первом рендеринге - это вызовет ошибку.
Скажите
return (<ul>
{['left', 'right'].map((el, i) =>
<li key={i}>
<input ref={refs.current[i]} value={el} />
{refs.current[i].current.value}</li> // cannot read property `value` of undefined
)}
</ul>)
Таким образом, вы либо охраняете его как
return (<ul>
{['left', 'right'].map((el, i) =>
<li key={i}>
<input ref={refs.current[i]} value={el} />
{refs.current[i].current && refs.current[i].current.value}</li> // cannot read property `value` of undefined
)}
</ul>)
, либо получаете доступ к useEffect
крючку.Причина: ref
s связаны после рендеринга элемента, поэтому при первом запуске рендеринга он еще не инициализирован.