私が理解している限り、次のような単一の要素にrefsを使用できます。
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>
要素の配列にこれをどのように実装できますか?明らかにそのようではない:(私はそれを試さなかったとしてもそれを知っていた:)
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>
私は this を見てきました。したがって this です。しかし、この単純なケースにその提案を実装する方法については、まだ混乱しています。
Refは最初は{ current: null }
オブジェクト。 useRef
は、コンポーネントレンダー間でこのオブジェクトへの参照を保持します。 current
値は、主にコンポーネントの参照を目的としていますが、何でも保持できます。
ある時点で参照の配列があるはずです。配列の長さがレンダリング間で異なる場合、配列はそれに応じてスケーリングする必要があります。
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>
);
このコードは、useEffect
をアンラップしてuseState
をuseRef
に置き換えることで最適化できますが、render関数で副作用を発生させることは一般的に悪い習慣と見なされます。
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>
);
あなたが ループ内でフックを使用することはできません として、配列が時間の経過とともに変化するときにそれを機能させるための解決策があります。
配列は小道具から来ていると思います:
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>
));
}
単純な理由でループ内でuseRefを使用しないでください。使用されるフックの順序が重要です!
ドキュメントは言う
ループ、条件、またはネストされた関数内でフックを呼び出さないでください。代わりに、常にReact関数のトップレベルでフックを使用してください。このルールに従うことで、コンポーネントがレンダリングされるたびにフックが同じ順序で呼び出されるようになります。これにより、Reactは、複数のuseState呼び出しとuseEffect呼び出しの間でフックの状態を正しく保持できます。 (ご興味があれば、以下で詳しく説明します。)
しかし、それが動的配列に明らかに適用されることを考慮してください...しかし、静的配列を使用している場合(常に同じ量のコンポーネントをレンダリングする場合)、それについてあまり気にしないで、何をしているのかに注意し、それを活用してください????
2つの方法があります
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>
配列(またはオブジェクト)を使用してすべての参照を追跡し、メソッドを使用して配列に参照を追加できます。
注:参照を追加および削除する場合は、レンダリングサイクルごとに配列を空にする必要があります。
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>
);
}
Renderメソッドが呼び出される前にrefが利用可能である必要があるため、stateは使用できません。 useRefを任意の回数呼び出すことはできませんが、一度呼び出すことはできます。
arr
が配列の小道具であると仮定します:
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();
}