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状态。

SS.png

再渲染不会被触发。

即使更改了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>
}

SS.png

你好只出现了一次并没有重新渲染。

可以跨越渲染器保存信息

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>
}

SS.png

当前的值每0.5秒不断增加,但hello并不频繁出现!

在 JavaScript 中使用 ref 操作 DOM

当点击聚焦按钮时,会使input标签获得焦点。

undefined
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传递给子组件。

undefined
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的声明式范式有点不同。虽然非常方便,但在使用时需要谨慎。我们应该经常思考“这样做真的好吗?”“是否有更好的方法?”

广告
将在 10 秒后关闭
bannerAds