こんにちは!
現在Reactでフロントエンドの開発を担当している土井です😎!
今回はDOM参照に使うref属性の使い方について、実際の業務で詰まった事と解決方法をご紹介したいと思います!
実現したいこと
ページからボタンを押下してダイアログを開く際に、下の画面イメージのように、ある一定の位置まで自動スクロールして表示したい。
ハマったこと
スクロール処理は、Element.ScrollTo()
で対応できるが、肝心のElementの参照はどうするか?
当初はuseRefを使って参照しようとした。(以下のような感じで↓)
//ダイアログのrefオブジェクト
const dialogRef = useRef(null)
// ダイアログを開けたら、スクロールするuseEffect
React.useEffect(() => {
if (open) {
if (dialogRef.current !== null) {
dialogRef.current.scrollTo(0, 2000); // スクロール処理
}
}
}, [open]); // 依存配列にはダイアログの開閉state
~~~~~~~~~~~~~~省略~~~~~~~~~~~~~~~~
<Dialog
open={open} // ダイアログの開閉state
onClose={handleClose}
scroll="paper"
aria-labelledby="scroll-dialog-title"
aria-describedby="scroll-dialog-description"
>
<DialogTitle id="scroll-dialog-title">Subscribe</DialogTitle>
<DialogContent
dividers
ref={dialogRef} // ref属性にrefオブジェクトをセット
>
しかし、ダイアログを開いた時は、dialogRef.current が nullになっておりスクロール処理が実行すころとが出来ない。
なぜdialogRef.currentがnullになっているのか?
原因
Dialogは常にDOM内には存在せず、Dialogを開いた際にマウントされるため。
openがtrueになった時点ではまだマウントされておらず、dialogRef.currentはnullになるらしい。
<補足>
マウント : DOMnodeがDOMツリーに追加されること
ちなみに混同しやすいものとして”レンダリング”もありますが、レンダリングはreactコンポーネントからDOMnodeを作るためにpropsとかstateなどが読み込まれる処理です。
解決方法
refオブジェクトはstateのように中身に変更があった際の通知はされないので、useEffectは使えない。
React公式にも記載があるが、ref属性にコールバック関数を渡して、そのコールバック関数の中でスクロール処理を実行することができる!
またコールバック関数の引数には、nodeを受け取ることが可能!!(←ここがポイント)
関数はref属性に更新がある(マウントする)度に呼び出されるので、
つまり、マウントされてnodeが参照できるようになった段階で何らかの処理が可能になる。
コールバック関数は、以下のように実装。
cost scrollDialog = useCallback((node) => { // 引数にnodeを受け取る
if (!node) return; // nodeがnullの場合はリターンして処理終了
if (node.scrollTop !== 2000) {
node.scrollTo(0, 2000); // スクロール処理
}
},[]);
~~~~~~~~~~~~~~省略~~~~~~~~~~~~~~~~
<Dialog
open={open} // ダイアログの開閉state
onClose={handleClose}
scroll="paper"
aria-labelledby="scroll-dialog-title"
aria-describedby="scroll-dialog-description"
>
<DialogTitle id="scroll-dialog-title">Subscribe</DialogTitle>
<DialogContent
dividers
ref={scrollDialog} // ref属性にコールバック関数をセット
>
React公式はこちら
最初は、なぜrefオブジェクトがnullになるのか原因が掴めず、解決に結構時間を要してしまった。
なんだかんだ公式を読むのはやはり大事と実感しました😄