1. ホーム
  2. reactjs

[解決済み] useImperativeHandle、useLayoutEffect、useDebugValueを使用する場合

2022-03-07 17:26:37

質問

なぜ、以下のようになるのか理解できません。 useImperativeHandle , useLayoutEffect および useDebugValue フックが必要なのですが、どのような場合に使用できるのか、例を挙げてください。

どのように解決するのですか?

前置きが長くなりましたが、これらのフックはすべて非常に稀にしか使われません。99%の場合、これらは必要ないでしょう。これらのフックは、稀なケースをカバーするためのものです。


useImperativeHandle

通常 useRef のコンポーネントのインスタンス値が渡されます。 ref が添付されています。これにより、DOM 要素と直接対話することができます。

useImperativeHandle は非常によく似ていますが、これは2つのことを可能にします。

  1. 返される値をコントロールすることができます。インスタンス要素を返す代わりに、戻り値を明示的に指定します (以下のスニペットを参照)。
  2. ネイティブ関数(例えば blur , focus など)に独自の関数を追加することで、通常の動作に副作用を与えたり、全く別の動作をさせることができます。ただし、その関数は好きなように呼べばいい。

ネイティブのプロパティを親に公開したくない、あるいはネイティブ関数の動作を変更したいなど、上記のいずれかを行いたい理由はさまざまでしょう。いろいろな理由が考えられます。しかし useImperativeHandle はほとんど使われません。

useImperativeHandle を使用する際に、親コンポーネントに公開されるインスタンス値をカスタマイズします。 ref

この例では、この値を元に ref には、関数 blur で宣言した useImperativeHandle . 他のプロパティは含まれません ( これを実証するために、値をログに記録しています ). この関数自体も、通常期待される動作とは異なる動作をするように "カスタマイズ"されています。ここでは document.title のときは入力をぼかします。 blur が呼び出されます。

const MyInput = React.forwardRef((props, ref) => {
  const [val, setVal] = React.useState('');
  const inputRef = React.useRef();

  React.useImperativeHandle(ref, () => ({
    blur: () => {
      document.title = val;
      inputRef.current.blur();
    }
  }));

  return (
    <input
      ref={inputRef}
      val={val}
      onChange={e => setVal(e.target.value)}
      {...props}
    />
  );
});

const App = () => {
  const ref = React.useRef(null);
  const onBlur = () => {
    console.log(ref.current); // Only contains one property!
    ref.current.blur();
  };

  return <MyInput ref={ref} onBlur={onBlur} />;
};

ReactDOM.render(<App />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>


useLayoutEffect

とある程度似ていますが useEffect() とは異なり、React が DOM に更新をコミットした後に実行されます。まれに、更新後に要素間の距離を計算したり、その他の更新後の計算や副作用を行う必要がある場合に使用します。

<ブロッククオート

と同じ署名です。 useEffect しかし、これはすべての DOM 変異の後に同期的に発生します。DOM からレイアウトを読み込んで、同期的に再レンダリングするために使用します。の中でスケジュールされた更新は useLayoutEffect は同期的にフラッシュされます。 をブラウザが描画する前に .

高さが変化する可能性のある絶対位置の要素があり、別の div その下にある。この場合 getBoundingCLientRect() を使用して、親の height および top プロパティを計算し、それらを子の top プロパティに適用するだけです。

ここでは useLayoutEffect ではなく useEffect . その理由は、以下の例でご確認ください。

useEffect : (飛び跳ねる動作に注意)

const Message = ({boxRef, children}) => {
  const msgRef = React.useRef(null);
  React.useEffect(() => {
    const rect = boxRef.current.getBoundingClientRect();
    msgRef.current.style.top = `${rect.height + rect.top}px`;
  }, []);

  return <span ref={msgRef} className="msg">{children}</span>;
};

const App = () => {
  const [show, setShow] = React.useState(false);
  const boxRef = React.useRef(null);

  return (
    <div>
      <div ref={boxRef} className="box" onClick={() => setShow(prev => !prev)}>Click me</div>
      {show && <Message boxRef={boxRef}>Foo bar baz</Message>}
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("app"));
.box {
  position: absolute;
  width: 100px;
  height: 100px;
  background: green;
  color: white;
}

.msg {
  position: relative;
  border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>

useLayoutEffect :

const Message = ({boxRef, children}) => {
  const msgRef = React.useRef(null);
  React.useLayoutEffect(() => {
    const rect = boxRef.current.getBoundingClientRect();
    msgRef.current.style.top = `${rect.height + rect.top}px`;
  }, []);

  return <span ref={msgRef} className="msg">{children}</span>;
};

const App = () => {
  const [show, setShow] = React.useState(false);
  const boxRef = React.useRef(null);

  return (
    <div>
      <div ref={boxRef} className="box" onClick={() => setShow(prev => !prev)}>Click me</div>
      {show && <Message boxRef={boxRef}>Foo bar baz</Message>}
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("app"));
.box {
  position: absolute;
  width: 100px;
  height: 100px;
  background: green;
  color: white;
}

.msg {
  position: relative;
  border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>


useDebugValue

特定の値やプロパティをデバッグしたい場合がありますが、そのためには高価な操作が必要で、パフォーマンスに影響を与える可能性があります。

useDebugValue は、React DevToolsが開いていて、関連するフックが検査されているときのみ呼び出され、パフォーマンスへの影響を防ぎます。

useDebugValue は、React DevToolsのカスタムフックのラベルを表示するために使用することができます。

個人的にはこのフックを使ったことがないのですが。多分、コメント欄の誰かが良い例を挙げて、何かヒントを与えてくれるでしょう。