React(TypeScript)でcanvas要素を扱う

## 背景
React(TypeScript)でcanvas要素を扱いたいと思ったところ、
Reactの場合だとどんなふうにコードを書けばいいか分からず、
色々と苦戦したので備忘録として残します。

## ゴール
canvas上で図形をドラックして動かせるようなサンプルを作ります。
![can.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332453/df759f7a-ac97-494b-2427-f4197d53fcf1.png)

## 結論
このように書くと、canvas上で赤い四角形をドラックして動かせるサンプルができます。
```typescript
import React, { useEffect, useRef, FC, useState } from "react";


export const Canvas:FC = () =>{

    const canvasRef = useRef<HTMLCanvasElement>(null);//canvas要素取得
    const [x,setX] = useState<number>(0);//X座砲
    const [y,setY] = useState<number>(0);//Y座標
    const [drug,SetDrug] = useState(false);//マウスドラック状態の管理



    useEffect(() => {
        if (!canvasRef.current) {
            throw new Error("canvas要素の取得に失敗しました");
        }
        const canvas = canvasRef.current;
        const ctx = canvas.getContext("2d");
        if (!ctx) {
            throw new Error("context取得失敗");
        }

        //描画前に既に描画済みのものを消してリセット
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

        //赤色の四角形を描画
        ctx.fillStyle = "red";
        ctx.fillRect(x, y, 10, 10);

    }, [x,y,drug]);



    const StartMove =()=>{
        SetDrug(true);
    }

    const EndMove = () => {
        SetDrug(false);
    };

    const Move = (e:any) => {
        //変数drugがtrueの場合座標を更新する
        if(drug){
            const canvas:any= canvasRef.current;
            const rect = canvas.getBoundingClientRect();//キャンバスの位置取得
            setX(e.clientX - rect.left);//マウスのX座標をセット
            setY(e.clientY - rect.top);//マウスのY座標をセット
        }
    };


    return(
        <>
            <div className="my-10" >
                <div className="border border-slate-700 p-3">
                    <h1 className="text-center text-3xl">キャンバス</h1>
                    <h2>座標</h2>
                    <p>x軸:{x}</p>
                    <p>y軸:{y}</p>
                    <canvas ref={canvasRef} 
                        width={400} 
                        height={450} 
                        onMouseDown={StartMove} 
                        onMouseUp={EndMove}
                        onMouseMove={Move}
                        className="border border-blue-700" 
                    />
                </div>
            </div>
        </>
    );
}
```

## ポイント
まずキャンバスを取得するには"useRef"で取得するのがいいようです。
canvasタグにidをつけてdeiElementByIdで取得もできますが、
状態管理する上ではこのやり方がいいみたいです。
```typescript
const canvasRef = useRef<HTMLCanvasElement>(null);//canvas要素取得
```
また、キャンバスに描画する際はuseEffect内で実行するのがいいようです。
今回のサンプルでは、座標(x,y)や描画可能か判定する変数drugが更新される度にuseEffectでanvasの描画を再実行することで四角形の描画位置を変更しています。
```typescript
    useEffect(() => {
        if (!canvasRef.current) {
            throw new Error("canvas要素の取得に失敗しました");
        }
        const canvas = canvasRef.current;
        const ctx = canvas.getContext("2d");
        if (!ctx) {
            throw new Error("context取得失敗");
        }

        //描画前に既に描画済みのものを消してリセット
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

        //赤色の四角形を描画
        ctx.fillStyle = "red";
        ctx.fillRect(x, y, 10, 10);

    }, [x,y,drug]);
```


【追記】
本記事を執筆後、Reactの公式docにてcanvasを使用しないやり方で
同じようなことができる記事を発見しました。
参考までに載せておきます。

https://ja.react.dev/learn/updating-objects-in-state