useRef 只是部分类似于 React 的 ref(只是对象的结构,只有 current 的字段)。
useRef 钩子旨在在渲染之间存储一些数据,并且更改这些数据不会触发重新渲染(与 useState 不同)。
也只是温馨提醒:最好避免在循环或if 中初始化挂钩。我是first rule of hooks。
考虑到这一点,我们:
-
创建数组并将其保存在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钩子中访问它。原因:refs 是在元素渲染后绑定的,所以在渲染期间第一次运行它还没有初始化。