useRef
is just partially similar to React's ref
(just structure of object with only field of current
).
useRef
hook is aiming on storing some data between renders and changing that data does not trigger re-rendering(unlike useState
does).
Also just gentle reminder: better avoid initialize hooks in loops or if
. It's first rule of hooks.
Having this in mind we:
create array and keep it between renders by useRef
we initialize each array's element by createRef()
we can refer to list by using .current
notation
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>)
}
This way we can safely modify array(say by changing it's length). But don't forget that mutating data stored by useRef
does not trigger re-render. So to make changing length to re-render we need to involve 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} />
</>)
}
Also don't try to access refs.current[0].current
at first rendering - it will raise an error.
Say
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>)
So you either guard it as
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>)
or access it in useEffect
hook. Reason: ref
s are bound after element is rendered so during rendering is running for the first time it is not initialized yet.
useRef(elts.map(React.createRef))
is exactly what I was looking for to keep the function component from throwing my refs away. β Marcelina