#React + fullcalendar + Steinでスプレッドシートをカレンダー化する

## Glideで予約表作ったら紙以下と言われたので仕方なくReactでつくってます

部内の会議室予約が紙ベースで嫌だったので、電子化することにしました。
あまり時間をかけたくないと考え、ノーコードツールの[glide](https://www.glideapps.com/)で作ったのですが、操作感がいまいちだったため、**紙以下**と言われてしまいました
頭にきたので**React + fullcalendarで一から作ってやろう**と言うのが前回記事でした。

https://qiita.com/watanabe-tsubasa/items/12892b4711bf1526051e

## スプレッドシートのデータをfullcalendarのフロントに反映させる

glideで作ったアプリはスプレッドシートをデータベースとして利用していた都合上、同じスプレッドシートのデータを読み込んで、Reactで作成したフロントに反映させることを考えました。
今回は[Stein](https://steinhq.com/)を用いて、スプレッドシートをAPI化して、データを反映させていきます。

https://steinhq.com/

### Stein

https://docs.steinhq.com/introduction

ドキュメントが非常にわかりやすいです。
```npm install stein-js-client```でインストールしてください。

## 実行環境

>Node.js version 18.1.0
>npm version 8.8.0
>
>"stein-js-client": "^0.0.2"
><details><summary>React周り</summary>
>
>  "react": "^18.1.0",
>  "react-admin": "^4.1.2",
>  "react-dom": "^18.1.0",
>  "react-scripts": "5.0.1",
>  "web-vitals": "^2.1.4"</details>
><details><summary>FullCalendar周り</summary>
>
>   "@fullcalendar/core": "^5.11.0",
>   "@fullcalendar/daygrid": "^5.11.0",
>   "@fullcalendar/list": "^5.11.0",
>   "@fullcalendar/react": "^5.11.1",
>   "@fullcalendar/timegrid": "^5.11.0",
>   "@fullcalendar/interaction": "^5.11.0",</details>

:::note info
前回記事から```fullcalendar/interaction```が増えてます
:::

## カレンダーへの反映

### カレンダー画面サンプル

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100048/dbfbd665-4b54-ea29-2e6f-fd08dcedabae.png" width=75%>

**スプレッドシート上のデータが反映されます**

### スプレッドシート

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2100048/6159a8a3-88cd-db13-821b-f62e20106719.png" width=75%>

スプレッドシートの1行目にcolumn名を入力していきます。
rowIdはGlideのアプリ側で自動的に設定されています。

:::note warn
開始終了の日時は**ISO 8601**形式で表示してください
:::

### サンプルコード

```jsx:CalendarData.jsx
import React, { useCallback, useState, useEffect } from "react";
import FullCalendar from "@fullcalendar/react";
import dayGridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from '@fullcalendar/timegrid';
import listPlugin from '@fullcalendar/list';
import interactionPlugin from "@fullcalendar/interaction";
import jaLocale from "@fullcalendar/core/locales/ja"

const now = new Date();

// steinでgoogle sheetから読み込み
const steinStore = require('stein-js-client')
const store  = new steinStore('stein_API_URL')//Steinで生成されたURLを入力

// カレンダーコンポーネントの生成
const CalendarData = (props) => {
  const [events, setEvents] = useState();
  //SteinAPIを叩く
  useEffect(() => {  
    store.read("シート1", { limit: 200, offset: 0 })  
       .then((data) => {
        console.dir(data);
        let eventList = data.map(element => {
          return {id: element.rowId, title: element.予約者, start:element.開始, end:element.終了, description:element.用途};
        });
        setEvents(eventList)
        });
  },[])

  return (
    events ?
    <FullCalendar
      plugins={[
        dayGridPlugin,
        timeGridPlugin,
        listPlugin,
        interactionPlugin
      ]}
      headerToolbar={{
        left: 'prev,next today',
        center: 'title', 
        right: 'dayGridMonth,timeGridWeek',//timeGridDayで単日表示も可能,listWeekでリスト表示
      }}
      initialView="timeGridWeek"
      initialDate={now.toISOString().split('T')[0]} //初期表示
      nowIndicator={true} //現在時刻の表示
      locales={[jaLocale]}  //todayとかも日本語化される
      locale="ja" // 日本語化
      selectable={true} //範囲指定が可能になる
      eventStartEditable={true} //ドラッグ&ドロップが可能になる
      eventDurationEditable={true} //期間変更
        
      events={events}
    /> : 
    <div><h1>読み込み中・・・ちょっと待ってね</h1></div>
  );
};
export default CalendarData;
```

解説が必要そうなところを折りたたみで解説していきます。

<details><summary>SteinAPIを叩く</summary>

```jsx
  const [events, setEvents] = useState();
  //SteinAPIを叩く
  useEffect(() => {  
    store.read("シート1", { limit: 200, offset: 0 })  
       .then((data) => {
        console.dir(data);
        let eventList = data.map(element => {
          return {id: element.rowId, title: element.予約者, start:element.開始, end:element.終了, description:element.用途};
        });
        setEvents(eventList)
        });
  },[])
```

Reactは`useEffect()`でAPIを叩いて、`setState`することでAPIの値をフロントに反映させることが多いです。

この記事がわかりやすいです。

https://terrblog.com/useeffect%E3%81%AE%E5%9F%BA%E6%9C%AC%E7%9A%84%E3%81%AA%E4%BD%BF%E3%81%84%E6%96%B9%E3%81%A8%E9%9D%9E%E5%90%8C%E6%9C%9F%E5%87%A6%E7%90%86%E3%81%AE%E3%82%84%E3%82%8A%E6%96%B9/


Steinで取得した```data```を```map```で```return```以下のようなオブジェクトを持つ配列として返し、```events```のstateに```setEvents```でセットしています。

このeventsという配列をこの後のコードで呼び出して、カレンダーに反映していきます。

</details>

<details><summary>SteinAPIを読み込んだらカレンダーを表示</summary>

```jsx
    events ?
    <FullCalendar

    /> : 
    <div><h1>読み込み中・・・ちょっと待ってね</h1></div>
```
```CalendarData```の```return```以下、長い部分を消すとこのようになります。
三項演算子で配列```events```が存在するときはカレンダーを表示、読み込まれるまではdivタグ内を表示するようにしています。

</details>

## 次はフロントで操作した内容をスプレッドシートに反映する

今回はカレンダーデータを読み込むところまで作成しました。
表示されたカレンダーを操作することはできますが、更新すると戻ってしまいます。

https://twitter.com/shuyin02/status/1568226759522529280

次はフロントの操作を反映させてみます。
もうできているので、あとは記事を書くやる気の問題ですね