【React】爆速でVexTabをReact-TypeScriptを用いて実装する

#初めに

こんにちは、クソ雑魚のなんちゃてエンジニアです。

我は最近ReactとTypeScriptを用いてフロントエンドの実装を行って遊んでおるのじゃ。
そこで、ギターとかでよく使われる「Tab譜」というものをWeb上で実装したくなったので、Reactで実装してみた。

その際の備忘録として記載する。

#VexTabとは？

https://makungoron-music.hatenablog.com/entry/vextab-introduce

上記ブログを見てもらったほうが早いだろう。
CDNで簡単にTab譜を自身のWebページで実装できるのである。

```html
<script src="https://unpkg.com/vextab/releases/div.prod.js" />
```
このスクリプトタグを記述し、

```html
<div class="vextab-auto" editor="true">
tabstave notation=true
</div>
```
この`div`タグを実装すればおしまいなのであるから、だいぶ簡単である。
出力は以下のようになる。（一例）
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2036848/e70e8c0d-cca6-5a28-441c-4e9a07975fe2.png)


#Reactで使用した技術について
特段変わったことはしていない。
名前空間を少々変更したり、useEffectで副処理の実行（ここら辺は後のコード見てもらえればわかりやすいかなと）をしたりします。

UIは以下の`@chakra-ui/react`を叩き込んでいるのであしからず。

https://chakra-ui.com/

では次項で具体的なコードを見てもらおう!

#コード

```react
import { Flex, Stack, Text, Textarea } from "@chakra-ui/react";
import { ChangeEvent, memo, useEffect, VFC} from "react";

type Props ={
  isEditor: boolean;
  data?: string;
  onChange: (e: ChangeEvent<HTMLTextAreaElement>)=>void;
} 

declare module 'react' {
  interface HTMLAttributes<T> extends DOMAttributes<T> {
      editor?: string
  }
}

export const VexTabComponent: VFC<Props> = memo((prop)=> {
  const {data, isEditor, onChange} = prop

  useEffect(() => {
    const head = document.getElementsByTagName('head')[0] as HTMLElement;
    const scriptUrl = document.createElement('script');
    scriptUrl.type = 'text/javascript';
    scriptUrl.src = 'https://unpkg.com/vextab/releases/div.prod.js';
    head.appendChild(scriptUrl);
}, [data]);

  return(
    <Flex>
    <Stack>
    <div className="vextab-auto" editor="false" style={{whiteSpace: 'pre-line'}}>
    {data}
    </div>
    {isEditor && (
      <>
      <Text>編集スペース</Text>
      <Textarea value={data} onChange={onChange} h="xl" />
      </>
    )} 
    </Stack>
    </Flex>
  );
  
});

```

各項目についていかにざっと説明を記載する。

```react
import { Flex, Stack, Text, Textarea } from "@chakra-ui/react";
import { ChangeEvent, memo, useEffect, VFC} from "react";
```
UIの「chakra-ui」しか特段Importしていないのがわかると思う。
ほぼ特殊なことはしていない。

```react
type Props ={
  isEditor: boolean;
  data?: string;
  onChange: (e: ChangeEvent<HTMLTextAreaElement>)=>void;
} 
```

親コンポーネントからのStateの型定義をTypeScriptさんで行っている。
`isEditor`はTabの編集スペース（TextArea）の表示該否を判定する。
`data`はTabの内容
`onChange`関数は言うまでもないだろう。

```react
declare module 'react' {
  interface HTMLAttributes<T> extends DOMAttributes<T> {
      editor?: string
  }
}
```
`div`タグで使用する`editor`を定義してるだけ。

```react
export const VexTabComponent: VFC<Props> = memo((prop)=> {
  const {data, isEditor, onChange} = prop
...
```
VexTabを使用するコンポーネントの定義。このコンポーネントを呼び出せば、どこからでもTabをページに反映できそうですね！ﾆｯｺﾘ
`memo`でレンダリング過多は防いでます。

```react
  useEffect(() => {
    const head = document.getElementsByTagName('head')[0] as HTMLElement;
    const scriptUrl = document.createElement('script');
    scriptUrl.type = 'text/javascript';
    scriptUrl.src = 'https://unpkg.com/vextab/releases/div.prod.js';
    head.appendChild(scriptUrl);
}, [data]);
```

`useEffect`でスクリプトタグを使用せずに、ユーザがTab内容を編集実施するたびにレンダリングとCDNでの通信を発生させるように実装。
`data`は何かDB等に保存されている設定なので、第二引数で指定。

```react
return(
    <Flex>
    <Stack>
    <div className="vextab-auto" editor="false" style={{whiteSpace: 'pre-line'}}>
    {data}
    </div>
    {isEditor && (
      <>
      <Text>編集スペース</Text>
      <Textarea value={data} onChange={onChange} h="xl" />
      </>
    )} 
    </Stack>
    </Flex>
  );

});
```

`editor=false`について、デフォルトでVexTabについてくるエディター機能よりはDBに保存している`data`を利用することを想定し、`TextArea`を使ってエディタ機能を実装した。
`whiteSpace: 'pre-line'`はVexTabさんが譜面を認識しやすいように改行を読み込ませるようにしました。
※それ以外は大体が「chakra-ui」なので、別途公式サイトをご覧ください。

#まとめ
ざっくりとTabをReact-TypeScriptで爆速実装する方法を記載した。
まぁこんな感じでフロントで遊んだので、備忘録で記載する。

※本業はフロントではござらん。。