2023年中重新熟悉一下useRef的使用
你好!我是株式会社エイチームライフデザイン的 @TMDM。
这篇文章是Ateam LifeDesign Advent Calendar 2023系列1的第9天的文章。
深入理解useRef的概念
使用useRef主要有两种用途。
-
- DOM要素への参照
Reactコンポーネント内の特定のDOM要素に参照できます。参照されたDOMはuseRefで操作が可能です。
データの保持
コンポーネントの再レンダリング間で、データを保持することが可能です。
useStateとは違って、値が変わっても再レンダリングされません。
考虑到上述内容,我们将实际操作并逐步理解官方的内容。
使用useRef
这是一个用于在渲染时引用不需要的值的 React 钩子。
- useRefに値を保持させていても、コンポーネントのレンダリングに直接影響をしませんよ!ということ。
使用方法: 利用这个方法进行操作。
和其他的hooks一样,该函数需要在顶层调用并使用。
import { useRef } from 'react';
const hoge = () =>{
const inputRef = useRef(null) // 引数に初期値
}
参数
在中文中,使用useRef(参数)作为current属性的初始值。
在第二次及以后的渲染中将被忽略,并返回相同的对象。
注意事项
-
- ref.currentはプロパティの書き換えが可能。(stateの一部を持っている場合は変更すべきではない。)
-
- ref.currentプロパティが変更されても、Reactは再レンダーしません。
- レンダー中にref.currentの値を読んだり書き込んだりはしてはいけない。コンポーネントの振る舞いが予測不可能になる(useState使おう)
使用ref关键字引用值
import { useRef } from 'react';
const hoge = () =>{
const intervalRef = useRef(0);
console.log(intervalRef)
useRef只有一个属性:current!current的日本语翻译是这样的:指定的初始值将返回设置的ref状态。
再渲染不会被触发。
即使更改了ref,也不会触发重新渲染。
真的吗?我将尝试使用以下代码进行确认。
import { FC, useEffect, useRef } from 'react'
export const Test: FC = () => {
const intervalRef = useRef(0)
const handleClick = () => {
intervalRef.current = intervalRef.current + 1
console.log(intervalRef.current)
}
useEffect(() => {
console.log('hello')
})
return <button onClick={handleClick}>クリック</button>
}
你好只出现了一次并没有重新渲染。
可以跨越渲染器保存信息
ref非常适合保存不会影响输出组件外观的数据。
// useStateと組み合わせてみる。
// useRefの値が増えてもレンダーされない。
import { FC, useEffect, useState, useRef } from 'react'
export const Kv: FC = () => {
const intervalRef = useRef(0)
const [count, setCount] = useState(0)
const handleClick = () => {
setCount((c) => c + 1)
console.log(count)
console.log(intervalRef.current)
}
setInterval(() => {
intervalRef.current = intervalRef.current + 1
}, 500) // 内部でuseRefのカウントだけどんどん進む
useEffect(() => {
console.log('hello')
})
return <button onClick={handleClick}>クリック</button>
}
当前的值每0.5秒不断增加,但hello并不频繁出现!
在 JavaScript 中使用 ref 操作 DOM
当点击聚焦按钮时,会使input标签获得焦点。
import React ,{useRef}from 'react'
const Sample = () => {
const inputRef = useRef(null)
const handleFocus =()=>{
inputRef.current.focus() // inputRefにフォーカスして
}
return (
<div>
<label>
<input type="text" ref={inputRef} />
<button onClick={handleFocus}>フォーカスボタン</button>
</label>
</div>
)
}
export default Sample
如果希望传递给组件的是 ref,可以使用 forwardRef 来传递。
有时候我们需要通过ref来操纵子组件的元素,对吧?
由于直接父组件无法访问其子组件的input,并且ref被处理得与普通props不同,因此无法使用props。因此,您需要使用forwardRef来将ref传递给子组件。
import React ,{useRef,forwardRef}from 'react'
const MyInput = forwardRef((props, ref) => { // 必ずこの順序(props, ref)で渡します
return <div>forwardRef<br/><input {...props} ref={ref} /></div>
});
const Sample = () => {
const inputRef = useRef(null)
const handleFocus =()=>{
inputRef.current.focus()
}
return (
<div>
<label>
<MyInput ref={inputRef} />
<button onClick={handleFocus}>フォーカスボタン</button>
</label>
</div>
)
}
export default Sample
forwardRef就像是门口一样。
forwardRef可以被视为提供给父组件的一种“进入口”,以方便访问其内部。
防止重新生成ref的值。
useRef用于保存传入的ref值,但每次渲染时都会发生调用。
const playerRef = useRef(new 重い処理()); // レンダーのたびに無駄が多い...
因此,建议进行初始化。
const playerRef = useRef(null);
if (playerRef.current === null) {
playerRef.current = new 重い処理(); // 初回レンダーだけ、重い処理をnewする。
}
除了公式以外,还介绍了一种利用useRef的技巧。使用React × IntersectionObserver来实现三个强大的功能!
- 広告の表示率計測 : コンテンツ作ったけど、ユーザーにどれくらい刺ささっているのか?を計測したいときに良さそうですね。
结束
辛苦了!useRef可以进行命令式操作。这与React的声明式范式有点不同。虽然非常方便,但在使用时需要谨慎。我们应该经常思考“这样做真的好吗?”“是否有更好的方法?”