Pour autant que je sache, je peux utiliser des références pour un seul élément comme celui-ci:
const { useRef, useState, useEffect } = React;
const App = () => {
const elRef = useRef();
const [elWidth, setElWidth] = useState();
useEffect(() => {
setElWidth(elRef.current.offsetWidth);
}, []);
return (
<div>
<div ref={elRef} style={{ width: "100px" }}>
Width is: {elWidth}
</div>
</div>
);
};
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Comment puis-je implémenter cela pour un tableau d'éléments? Évidemment pas comme ça: (je le savais même je ne l'ai pas essayé :)
const { useRef, useState, useEffect } = React;
const App = () => {
const elRef = useRef();
const [elWidth, setElWidth] = useState();
useEffect(() => {
setElWidth(elRef.current.offsetWidth);
}, []);
return (
<div>
{[1, 2, 3].map(el => (
<div ref={elRef} style={{ width: `${el * 100}px` }}>
Width is: {elWidth}
</div>
))}
</div>
);
};
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
J'ai vu this et donc this . Mais, je suis toujours confus sur la façon de mettre en œuvre cette suggestion pour ce cas simple.
Une référence est initialement juste { current: null }
objet. useRef
conserve la référence à cet objet entre les rendus de composants. current
value est principalement destinée aux références de composants mais peut contenir n'importe quoi.
Il devrait y avoir un tableau de références à un moment donné. Si la longueur du tableau peut varier d'un rendu à l'autre, un tableau doit évoluer en conséquence:
const arrLength = arr.length;
const [elRefs, setElRefs] = React.useState([]);
React.useEffect(() => {
// add or remove refs
setElRefs(elRefs => (
Array(arrLength).fill().map((_, i) => elRefs[i] || createRef())
));
}, [arrLength]);
return (
<div>
{arr.map((el, i) => (
<div ref={elRefs[i]} style={...}>...</div>
))}
</div>
);
Ce morceau de code peut être optimisé en dépliant useEffect
et en remplaçant useState
par useRef
mais il convient de noter que faire des effets secondaires dans la fonction de rendu est généralement considéré comme une mauvaise pratique:
const arrLength = arr.length;
const elRefs = React.useRef([]);
if (elRefs.current.length !== arrLength) {
// add or remove refs
elRefs.current = Array(arrLength).fill().map((_, i) => elRefs.current[i] || createRef())
));
}
return (
<div>
{arr.map((el, i) => (
<div ref={elRefs.current[i]} style={...}>...</div>
))}
</div>
);
Comme vous ne pouvez pas utiliser de hooks dans les boucles , voici une solution pour le faire fonctionner lorsque le tableau change au fil du temps.
Je suppose que le tableau provient des accessoires:
const App = props => {
const itemsRef = useRef([]);
// you can access the elements with itemsRef.current[n]
useEffect(() => {
itemsRef.current = itemsRef.current.slice(0, props.items.length);
}, [props.items]);
return props.items.map((item, i) => (
<div
key={i}
ref={el => itemsRef.current[i] = el}
style={{ width: `${(i + 1) * 100}px` }}>
...
</div>
));
}
Notez que vous ne devez pas utiliser useRef dans une boucle pour une raison simple: l'ordre des hooks utilisés est important!
La documentation dit
N'appelez pas Hooks à l'intérieur de boucles, de conditions ou de fonctions imbriquées. Au lieu de cela, utilisez toujours des crochets au niveau supérieur de votre fonction React. En suivant cette règle, vous vous assurez que les crochets sont appelés dans le même ordre à chaque rendu d'un composant. C'est ce qui permet React pour préserver correctement l'état des Hooks entre plusieurs appels useState et useEffect. (Si vous êtes curieux, nous expliquerons cela en détail ci-dessous.)
Mais considérez que cela s'applique évidemment aux tableaux dynamiques ... mais si vous utilisez des tableaux statiques (vous restiturez TOUJOURS la même quantité de composants) ne vous en faites pas trop, soyez conscient de ce que vous faites et tirez-en parti ????
Il y a deux façons
const inputRef = list.map(x => useRef(null));
inputRef[idx].current.focus();
<input
ref={inputRef[idx]}
/>
const {useRef} = React;
const App = () => {
const list = [...Array(8).keys()];
const inputRef = list.map(x => useRef(null));
const handler = idx => () => {
const next = inputRef[idx + 1];
if (next) {
next.current.focus();
}
};
return (
<div className="App">
<div className="input_boxes">
{list.map(x => (
<div>
<input
key={x}
ref={inputRef[x]}
onChange={handler(x)}
type="number"
className="otp_box"
/>
</div>
))}
</div>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
const inputRef = useRef([]);
inputRef.current[idx].focus();
<input
ref={el => inputRef.current[idx] = el}
/>
const {useRef} = React;
const App = () => {
const list = [...Array(8).keys()];
const inputRef = useRef([]);
const handler = idx => e => {
const next = inputRef.current[idx + 1];
if (next) {
next.focus()
}
};
return (
<div className="App">
<div className="input_boxes">
{list.map(x => (
<div>
<input
key={x}
ref={el => inputRef.current[x] = el}
onChange={handler(x)}
type="number"
className="otp_box"
/>
</div>
))}
</div>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
Vous pouvez utiliser un tableau (ou un objet) pour garder une trace de toutes les références et utiliser une méthode pour ajouter une référence au tableau.
REMARQUE: si vous ajoutez et supprimez des références, vous devrez vider le tableau à chaque cycle de rendu.
import React, { useRef } from "react";
const MyComponent = () => {
// intialize as en empty array
const refs = useRefs([]); // or an {}
// Make it empty at every render cycle as we will get the full list of it at the end of the render cycle
refs.current = []; // or an {}
// since it is an array we need to method to add the refs
const addToRefs = el => {
if (el && !refs.current.includes(el)) {
refs.current.Push(el);
}
};
return (
<div className="App">
{[1,2,3,4].map(val => (
<div key={val} ref={addToRefs}>
{val}
</div>
))}
</div>
);
}
exemple de travail https://codesandbox.io/s/serene-hermann-kqps
Nous ne pouvons pas utiliser state car nous avons besoin que la référence soit disponible avant l'appel de la méthode de rendu. Nous ne pouvons pas appeler useRef un nombre arbitraire de fois, mais nous pouvons l'appeler une fois:
En supposant que arr
est un accessoire avec le tableau des choses:
const refs = useRef([]);
// free any refs that we're not using anymore
refs.current = refs.current.slice(0, arr.length);
// initialize any new refs
for (let step = refs.current.length; step < arr.length; step++) {
refs.current[step] = createRef();
}