useRef
n'est que partiellement similaire à l'outil de React ref
(juste la structure de l'objet avec seulement le champ de current
).
useRef
a pour but de stocker certaines données entre les rendus et la modification de ces données ne déclenche pas un nouveau rendu (contrairement aux useState
fait).
Un petit rappel : il vaut mieux éviter d'initialiser les hooks dans des boucles ou des if
. C'est première règle des crochets .
C'est dans cet esprit que nous :
-
créer un tableau et le conserver entre les rendus en useRef
-
nous initialisons chaque élément du tableau par createRef()
-
nous pouvons nous référer à la liste en utilisant .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>)
}
De cette façon, nous pouvons modifier le tableau en toute sécurité (par exemple en changeant sa longueur). Mais n'oubliez pas que la mutation de données stockées par useRef
ne déclenche pas le re-rendu. Ainsi, pour que le changement de longueur déclenche un nouveau rendu, nous devons impliquer 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} />
</>)
}
N'essayez pas non plus d'accéder refs.current[0].current
au premier rendu - cela entraînera une erreur.
Dites
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>)
Donc soit vous le gardez comme
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>)
ou y accéder dans useEffect
crochet. Raison : ref
sont liés après le rendu de l'élément, de sorte que lorsque le rendu est exécuté pour la première fois, ils ne sont pas encore initialisés.