React の Ref は親コンポーネントから子コンポーネントを参照し、任意の操作を可能にする機能です。
Ref と DOM – React
例えば、次のようなコードが実現できます。
/*** ref される側 ***/
import React from "react";
/**
*プレイヤー
* ただ状態とその操作用メソッドがあるのみです
*/
export class Player extends React.Component {
/**
* コンストラクタ
* @param props
*/
constructor(props) {
super(props);
this.state = {
text: "stop"
};
this.play = this.play.bind(this);
}
/**
* 再生
*/
play() {
this.setState({ text: "play" });
}
/**
* 一時停止
*/
pause() {
this.setState({ text: "pause" });
}
/**
* 描画
*/
render() {
return <div>{this.state.text}</div>;
}
}
/*** ref で呼び出す側 ***/
import React, { Fragment } from "react";
import { Player } from "./Player";
/**
* プレイヤーコントローラ
*/
export class CallPlayer extends React.Component {
playerRef = React.createRef();
/**
* 描画
*/
render() {
return (
<Fragment>
<Player ref={this.playerRef} />
{/* クリックしたら ref.current.メソッド名 で子コンポーネントのメソッドを実行 */}
<button onClick={() => this.playerRef.current.play()}>play</button>
<button onClick={() => this.playerRef.current.pause()}>pause</button>
</Fragment>
);
}
}
これを動かしたデモが次です。
こんな感じで呼び出し側のコンポーネントから呼び出されている側を制御できます。
次の引用にある注意書きの様に多くのコンポーネントでこの機能は使うべきでありません。特に state を変化させるコンポーネントは暗黙のうちに呼び出されるメソッドの順番に依存している場合が多々あります(不意にメソッドを好き勝手動かすと意図せぬ state の変化が起きます)。また一見使用されていないメソッドが実は親側で使用されているのでは、と気にする必要も出てくるので混乱の元になります。
Ref を使いすぎない
最初はアプリ内で「何かを起こす」ために ref を使いがちかもしれません。そんなときは、少し時間をかけて、コンポーネントの階層のどこで状態を保持すべきかについて、よりしっかりと考えてみてください。多くの場合、その状態を「保持する」ための適切な場所は階層のより上位にあることが明らかになるでしょう。具体例については state のリフトアップガイドを参照してください。
Ref と DOM – React#Ref を使いすぎない
それでも時折、呼び出し側から命令されることが前提となる DOM 要素やライブラリがあります。そういった際には Ref の出番です。特に video, audio といったメディアを操作する DOM 要素はその傾向が強く、動画プレイヤーなどを作る時には Ref に頼ることになるでしょう。
<audio>: 埋め込み音声要素 – HTML: HyperText Markup Language | MDN
<video>: 動画埋め込み要素 – HTML: HyperText Markup Language | MDN
また、Refを使うことで呼び出し側から既存のライブラリコンポーネントに想定されていない操作を無理矢理行うことができるのでハック的な使い方もできます(ライブラリのバージョンアップで壊れやすいので最終手段的ですが)。