React開発入門③ 〜モーダルダイアログの実装(useRef)～

## 本ページについて
本ページではReactとNode.jsを用いた超簡潔な勤怠登録アプリケーションを開発します。
環境構築および動作確認は完了しているものとします。

### 前提
・React/Node.jsの環境構築および基礎の理解が完了している。
・プログラミングに関する基礎知識を備えている。（条件分岐、ループ処理、変数定義/代入、関数、クラスなど）
・javascriptの基礎文法を理解している。
・html/cssの基礎を理解している。

### 成果物のイメージ

・ボタンの数
ボタンは『勤務開始』『勤務終了』の２つを配置する。

・ボタンの挙動
クリック：モーダルダイアログが開き、打刻完了のメッセージが表示される。

## ボタンを作成する
まずはボタンを配置しましょう。
ボタンは\<button>要素で定義します。
```PunchTimeClock.jsx
export const PunchTimeClock = () =>{
    return (
        <>
            <h2>タイムレコーダー</h2>
            <button>勤務開始</button>
            <button>勤務終了</button>
        </>
    );
};
```
勤務開始と勤務終了の２つのボタンを定義しました。
ブラウザで確認してみましょう。
![スクリーンショット 2024-10-17 115704.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3801383/5caa66d1-4db5-a3cd-81a3-cfacb0838efe.png)
ボタンが２つ追加されています。
しかし、クリックしても何も起こりません。

## ボタンクリックでイベント(ダイアログ立ち上げ)を起こす。
ボタンクリックでダイアログを立ち上げてみましょう。
PunchTimeClock.jsxを下記コードにします。
```PunchTimeClock.jsx
import React from 'react';

export const PunchTimeClock = () => {

    // ダイアログ制御関数
    const openDialog = (title, message) => {
        const dialog = document.getElementById("myDialog");
        dialog.querySelector("h2").textContent = title;
        dialog.querySelector("p").textContent = message;
        dialog.showModal();
    };
    const closeDialog = () => {
        const dialog = document.getElementById('myDialog');
        dialog.close();
    };

    return (
        <div>
            <h2>タイムレコーダー</h2>
            <button id="startWorkBtn" onClick={() =>{
                openDialog("勤務開始","打刻完了");
            }}>勤務開始</button>

            <button id="FinishWorkBtn" onClick={() =>{
                openDialog("勤務終了","打刻完了");
            }}>勤務完了</button>

            <dialog id="myDialog">
                <h2>ダイアログのタイトル挿入</h2>
                <p>ダイアログのメッセージ挿入</p>
                <button onClick={closeDialog}>閉じる</button>
            </dialog>
        </div>
    );
};
```
急にコードが長くなりましたが、ざっくり解説します。

ダイアログは\<dialog>要素で定義します。
\<h2>はダイアログの題名、\<p>はダイアログのメッセージです。
これらの内部テキストはプレースホルダーにしておき、ダイアログが表示時に呼び出されるopenDialog関数でtitleとmessageの２つの引数を埋め込むようにしています。
こうすることで\<dialog>要素を複数定義することなく、『勤務開始』と『勤務終了』の２つのダイアログ表示パターンを使い分けることができます。

動作確認をしてみます。
![スクリーンショット 2024-10-15 233151.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3801383/1de1104f-2d69-6323-6a3b-019281a453e6.png)
↑勤務開始ボタンを押した場合

![スクリーンショット 2024-10-15 233217.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3801383/c60c01c0-825f-626f-e9d9-216a6ea369af.png)
↑勤務終了ボタンを押した場合

閉じるボタンを押すとしっかりと閉じられることも確認してみてください。

## 補足.モーダルダイアログについて
```
dialog.showModal();
```
正確なことを言うと、dialog.showModal()は　モーダルダイアログ　を開きます。

::: note info
モーダルダイアログ
ウィンドウ内の指示に従うかキャンセルしない限り他の操作ができなくなるダイアログのこと。
:::

試しにdialog.show()に書き換えてダイアログとの動作確認をしてみてください。
dialog.show()で開かれた方は、ダイアログが開いている間でも他のボタンをクリックしたりできます。
覚えておいて損はないのでユースケースに応じて使い分けてみてください。

## useRefを使ってもう少しコンパクトに書いてみる
上のコードでも動作はするのですが、少しコンパクトさに欠けます。
例えば、
const dialog = document.getElementById("myDialog");
HTMLのdialog要素を参照するための変数が２回定義されています。
参照情報を変数に保持し、どの関数でも共通で使えるようにします。

HTML要素への参照を変数に保持させるには　useRef　というモジュールを使います。
HTML要素にref属性を付与してuseRef関数で呼び出せば、HTML要素の情報を変数として保持できます。
さらに、useRefのデータが変更されてもコンポーネントは再レンダリングされません。


useRefを用いてコンパクトにしたコードが下記となります。
動作は先ほどと変わりません。
```PunchTimeClock.jsx
import React, { useRef } from 'react';

export const PunchTimeClock = () => {

    // useRefでの要素参照
    const dialogRef = useRef();

    // ダイアログ制御関数
    const openDialog = (title, message) => {
        dialogRef.current.querySelector("h2").innerHTML = title;

        dialogRef.current.querySelector("p").innerHTML = message;
        dialogRef.current.showModal();
    };
    const closeDialog = () => dialogRef.current.close();


    return (
        <div>
            <h2>タイムレコーダー</h2>
            <button id="startWorkBtn" onClick={() => {
                openDialog("勤務開始", "打刻完了");
            }}>勤務開始</button>

            <button id="FinishWorkBtn" onClick={() => {
                openDialog("勤務終了", "打刻完了");
            }}>勤務終了</button>

            {/* useRef参照用のref属性を追加 */}
            <dialog ref={dialogRef}>
                <h2>insert_dialogTitle!</h2>
                <p>insert_dialogMassage!</p>
                <button onClick={closeDialog}>閉じる</button>
            </dialog>
        </div>
    );
};
```

以上となります。

### Next
まだ決まってません。
ヘッダーとかカレンダーとかユーザ認証周りまではやっていきたいです。