`useRef`と` createRef`の違いは何ですか?


119

に出くわしたとき、私はフックのドキュメントを調べていましたuseRef

彼らの例を見て…

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

…にuseRef置き換えることができるようcreateRefです。

function TextInputWithFocusButton() {
  const inputRef = createRef(); // what's the diff?
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputRef.current.focus();
  };
  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

参照用のフックが必要なのはなぜですか?なぜuseRef存在するのですか?

回答:


152

違いは、createRef常に新しい参照を作成することです。クラスベースのコンポーネントでは、通常、構築中にインスタンスプロパティにrefを配置します(例this.input = createRef())。関数コンポーネントにはこのオプションはありません。useRef最初のレンダリング時と毎回同じ参照を返すように処理します。

これら2つの関数の動作の違いを示すサンプルアプリを次に示します。

import React, { useRef, createRef, useState } from "react";
import ReactDOM from "react-dom";

function App() {
  const [renderIndex, setRenderIndex] = useState(1);
  const refFromUseRef = useRef();
  const refFromCreateRef = createRef();
  if (!refFromUseRef.current) {
    refFromUseRef.current = renderIndex;
  }
  if (!refFromCreateRef.current) {
    refFromCreateRef.current = renderIndex;
  }
  return (
    <div className="App">
      Current render index: {renderIndex}
      <br />
      First render index remembered within refFromUseRef.current:
      {refFromUseRef.current}
      <br />
      First render index unsuccessfully remembered within
      refFromCreateRef.current:
      {refFromCreateRef.current}
      <br />
      <button onClick={() => setRenderIndex(prev => prev + 1)}>
        Cause re-render
      </button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

1rvwnj71x3を編集する


38

createRef常に新しい参照を返します。これは通常、クラスコンポーネントのインスタンスのフィールドとして保存します。機能コンポーネントのインスタンスのレンダリングごとに同じ参照useRef返します。これにより、明示的にどこにも保存しなくても、参照の状態をレンダリング間で維持できます。

2番目の例では、レンダリングのたびに参照が再作成されます。


これは正しくありません。ステートメントをバックアップするための参照がありますか?
AdeelImran19年

1
React開発者の1人による、これがどのように機能するかを説明するコメントがあります:reddit.com/r/reactjs/comments/a2pt15/…この回答についてあなたが間違っていると思うことを知りたいと思います。
ジョー・クレイ

この質問に答える前にそのリンクを見ましたが、あなたが共有したリンクのどこにこの事実が記載されていますか?見つかりませんでしたか?:)
AdeelImran19年

1
私が共有したリンクuseRefは、React開発者の1人によって投稿されたの簡略化された実装を示しています。それは単に呼び出しと同じではないcreateRefとして、createRefフックではなく、呼び出しの間の任意の状態を持続しません。ライアン・コグズウェルの答えにも違いの良い例があります。
ジョー・クレイ

1
そのコンテキストからの私の理解は、useRefが内部でcreateRefを使用するカスタムフックであることを推測しただけです。知識を共有していただきありがとうございます。
AdeelImran19年

12

tldr

ArefはプレーンなJSオブジェクト{ current: <some value> }です。

React.createRef()refを返すファクトリです{ current: null }-魔法は関係していません

useRef(initValue)また、に{ current: initValue }類似したrefを返しますReact.createRef()さらに、この参照が関数コンポーネント内の複数のレンダリングにわたって永続的であることをメモします

React.createRefrefオブジェクトはインスタンス変数に割り当てられている ため、クラスコンポーネントで使用するだけで十分です。したがって、コンポーネントとそのライフサイクル全体でアクセスできます。
this.myRef = React.createRef(); // stores ref in "mutable" this context (class)

useRef(null)基本的 useState(React.createRef())[0] 1に相当します。


1置き換えuseRefuseState+createRef

次のツイートは私にとって啓発的でした:

useRef()基本的にuseState({current: initialValue })[0]です。

このtldrセクションからの洞察により、さらに結論を出すことができます。

useRef(null)基本的にuseState(React.createRef())[0]です。

上記のコードuseStateは、から返された参照を永続化するために「悪用」しReact.createRef()ます。-[0]の値の部分を選択するだけで、セッターになります。useState[1]

useStateとは対照的に、再レンダリングを引き起こしますuseRef。より正式には、ReactuseStateは、setterメソッドを介して新しい値が設定されると、の古いオブジェクト参照と新しいオブジェクト参照を比較します。私たちがした場合に変異の状態useState(セッターの呼び出しではなく)直接に、その行動は、多かれ少なかれなっ同等useRef何の再レンダリングしていないとして、もうトリガーされます。

// Example of mutaing object contained in useState directly
const [ref] = useState({ current: null })
ref.current = 42; // doesn't cause re-render

注:これを行わないでください!useRef車輪の再発明の代わりに、最適化されたAPIを使用してください。上記は説明のためのものです。


3

目的を強調するためだけに:

createRefと同じくらい簡単return {current: null}です。これref=は、最も現代的な方法で小道具を処理する方法であり、それだけです(文字列ベースはあまりにも魔法であり、コールバックベースはあまりにも冗長に見えます)。

useRefレンダリング前に一部のデータを保持し、それを変更しても(のようにuseState)再レンダリングは発生しません。それらはめったに関連していません。クラスベースのコンポーネントがインスタンスfields(this.* =)に移動することを期待するものはすべてuseRef、機能コンポーネントに実装される候補のように見えます。

SayuseCallbackは有界クラスメソッド(this.handleClick = .....bind(this))として機能し、で再実装できます(ただし、ホイールを再発明するべきではありません)useRef

別の例としては、DOM参照、タイムアウト/間隔ID、サードパーティライブラリの識別子または参照があります。

PS Reactチームは、とのuseRef混同を避けるために、別の名前を選択した方がよいと思いますcreateRef。多分useAndKeepあるいはusePermanent


1

他の人の答えへのさらに別の、しかし重要な追加。

に新しい値を設定することはできませんcreateRef。しかし、あなたはのためにできますuseRef

const ur = useRef();
const cr = createRef();

ur.current = 10; // you can do it, and value is set
cr.current = 10; // you can, but it's no good, it will not change it

refはプレーンオブジェクトなので、current通常どおりプロパティを変更できます(テストしたばかりです)。参照がuseRefまたはを介して作成されているかどうかは関係ありませんcreateRef
ford 0420年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.