
React.js & GASを用いたAPI連携
あはぁ…React楽しいね(吐血)
どうも、めっどべいです。本来なら1週間に1本アプリを作ろうと思っていたのですが、今回のアプリがあまりに時間がかかり、2週間半が立ちました。アパー(白目)
でも、今回の苦しみによってReactのハンドラー、そしてAPIと仲良くなれました。(多分…)APIのエンドポイントを立ててAPI連携を行ったのが今回のハイライトです。Reactでアプリを作成する上でかなり大事になってくると思うので、前回の補足のような形で重点的に記載して行きます。
前回のあらすじ
前回、Githubの猛攻を耐え凌いだめっどべい。なんとかメンバーによるデバッグ・差し戻し地獄の果てに、なんとかアプリをひとつ完成させることに成功した。今回は前回の使いまわしで簡単にアプリを作ろうと考えているめっどべいにどんな地獄が待っているのか??
簡単にアプリを作れると思っていた頃が私にもありました
前回、ApiからJsonファイルを引っ張ってくることに成功しためっどべいは「なんだ、意外と簡単なWebアプリなら開発できるじゃ〜ん ♫」
と調子に乗っていた。1週間に1アプリ作ると決めていた私は、前回の形式を流用し簡単な占いのアプリを作ろうと画策していたのだ。まずは今回のアプリに必要な技術要旨をまとめてみた。
Webサイトから占いのデータをGetする
→前回、JsonファイルをApiから引っ張ってきたので流れは大体わかるバックエンドで入力データを計算
→計算のロジックはもうできてるからJSに移行するだけ()計算済みデータを表示できる形に整形
→整形なんてヨユーっしょ(慢心)持ってきたデータを画面に表示
→よくわからんが前回みたいにuseState使えばええやろ表を作って画面に表示
→forループを回して二次元配列表示すればいいんやな
うんいけそうだな()…私はそう思ってしまった。
これが終わりの始まりだった。20日に及ぶ格闘が始まるとはいざ知らず…
apiの取得が…できない?!
まず私は、以下の記事を見つけた。これを見て私は思った。「同じことをReactですればいいじゃん」…と。幸い、私はGASも書ける。私の経験からすればGASの中身はほぼJavaScriptだ。移管も簡単。勝ったな()
しばらく時間をかけてせっせとGASをJavaScriptに書き直した。まあ、楽勝だな、オレ、天才だしな…とイキりつつホットリロードボタンをポチ。
…何も画面に表示されない。

天才は早くも死んだ。まず、「Webサイトから占いのデータをGet」しようとしたが画面にもコンソールにも何も出てこない。ブレイクポイントを切り、プログラムを小分けにしつつ数時間かけてデバッグをする。この時点ですでに半泣きである。数時間かけてたどり着いたのは「await」(非同期処理)が入ると動かなくなる、というところまでだった。
デバッグツールと睨めっこしても何も進まず時間だけが溶けていく。集中力も気力もメンタルも折られた自称天才は、そのままふて寝をキメた。
いい子は最初にデベロッパーツールを開こう
何も進まず2日が経った。このアプリから現実逃避していた私は以前作ったアプリの修正をしていた。レスポンシブデザインの確認をするためにデベロッパーツールを開いた私は、思いがけないものと出会った。

コンソール、あるやんけーーーー!!
そこには見つけられなかったはずのコンソールがあった。
「なんや生きとったんかワレぇ…」
私は生き別れた双子の兄妹との再会を果たしたように咽び泣いた。深夜28:00の土曜日である。
まさか、エラーログがVSCodeじゃなくてHTMLの開発者画面に出てくるなんて思わんやん…いや、私が知らなかっただけなのかもしれん。
Cross-Origin Resource Sharing error
そこにはこんなエラーが書かれていた。なんだこれ?教えてチャットGPTくーん!
CORSの制限は認められていない異なるオリジンからの不正アクセスを禁止するという目的で設定されています。CORSの制限解除にはサーバー側の設定によりアクセスパスを通す必要があります。
簡単にまとめると、「フロントエンドから他のサイトのHTMLにGetを飛ばすことはできない」ということだ。アバ〜(卒倒)
つまり? 自分でバックエンドのサーバーを準備し? APIを噛ませて?
GetしたHTMLをフロントエンドに送り返さなきゃいけないってこと??
ちょっと待ってくれ。
突然技術難易度上がったが???
まずは画面遷移の話をしよう
まず、画面表示の方法が悪かった。
Reactというのは画面をDomによって変更部分だけ更新し最適化処理をすることによってページ表示の高速化をしているらしい(知らんけど)
基本のuseState
基本、画面の遷移やリロードによって入力されている変数はリセットされてしまう仕組みとなっている。そのため、無闇に変数を使ったところで画面に表示させることはできない。
そのため「useState」という画面の遷移によって消えない状態変数を用意する必要がある。まあ、わかりやすくいえば「画面遷移版のグローバル変数」って感じか?
const [data, setData] = useState([]); //配列の場合
const [message, setMessage] = useState(''); //変数単体の場合
他の変数とは違い、useState変数は直接代入することはできない。
その代わり、変数の宣言とセットで宣言した「set〇〇()」によって変数を書き換える。(直接代入するとDOMが変数の変化を検出できないため適切に画面を書き換えることができないから、らしい)
基本的に「変数の準備 → set〇〇 → 変数として使用」の手順で使用する。これさえ押さえておけば基本はオッケー!
また、すでにセットしてあるuseState変数のスコープは定義したファイル内なのでグローバル変数のように扱うこともできる。
配列のuseState変数
また、配列として定義したuseState変数は、ひとつづつ値を更新するのではなく、まとめて値を変更し更新しなければ反映されない。
どうやら、(一括変更ではなく)連続でひとつずつ変数を変更すると最後のひとつしか反映されないらしい。なので、以下のコードのようにまとめて値を書き換えてから「setItem」するのが良いだろう
//配列useState変数の宣言
const [color_item, setColorItems] = useState([
{ id: 1, value: 'color1' },
{ id: 2, value: 'color1 value' },
{ id: 3, value: 'color2' },
{ id: 4, value: 'color2 value' },
{ id: 5, value: 'color3' },
{ id: 6, value: 'color3 value' },
]);
//値を変更する関数
const culc_color = async () => {
var color_kari = ['','','','','',''];
color_kari = colorFunction.calc_uranai(message);
const updatedColor = fate_item.map(item => {
//swich文でまとめて変更する
switch (item.id) {
case 1:
return { ...item, value: "赤"};
case 2:
return { ...item, value: 赤は激しい色だよ};
case 3:
return { ...item, value: 青};
case 4:
return { ...item, value: 青は冷たい色だね};
case 5:
return { ...item, value: 緑};
case 6:
return { ...item, value: 緑は優しい色だ};
default:
return item;
}
})
//最後に一括で代入する
setColorItems(updatedColor);
}
配列のuseState変数に入れてしまったものは、普通の配列のように
「 Item[4] 」のようにして取り出すことができる。
API ?何それ美味しいの?
話を元に戻す。進み始めた道のりが茨の道だとはいえ、もう止まることはできないのだ。(すでに血まみれだが…)制作の手は止まらない。
なんとか検索をした中で、いけそうな方法が見つかった
GASをサーバーレスのバックエンドに見立ててデータをGetし、それをエンドポイントとしてフロントエンドに送り返すというものだった。うーんわからんがこれでやるしかない。
api、通らず。
doGet関数を書き、GASで書いたプログラムをWebアプリとしてデプロイし、データを送り返す。…何も、表示されない。
なんでや!!!!!!!!(台パン)
落ち着け落ち着け、まずは冷静にデータがGetされているかの確認だ。
以下のPostMan というサイトでGet データが送られているか確認しよう。
このサイトはGETやPOSTリクエストを送り、その内容を確認できるというサイトだ。ちゃんとリクエストが送られていればその結果が画面の下部に表示されるはず…以下のサイトの通りにやったら通るはず…

されてないやんけーーーーーー!!!!(2回目)
されて、ない。ない。表示されてなーーーーい!
この時点で発狂寸前である。
うっすらキレながらデバックを進めると、どうやら認証が通ってないらしいことに気がついた。しかも、GASで変更したコードの変更点も反映されてない。ナンデ????
大馬鹿者のミス

よーーーーーくGASを見てみると、デプロイの画面に「バージョン1」と書いてある。しかもバージョン1とバージョン2で発行されるURLが違う。
そう、GASは仕様上、バージョン管理からデプロイしなおさないと変更点が反映されないのだ。まあホットリロードじゃないから当たり前なのだが、ここまでホットリロード前提でコードを書いていたから、気づかなかった。
バージョンを更新する際にアクセス権の確認も行われたので、これで通るはず。お願いしまーーーーーーす!!!(◯マーウォーズ感)

き、きたーーーー!!!
やりました!!!HTMLが取得できています!!!!
これで、reactでもHTMLが受け取る準備ができた!
いい感じにデータを受け取る関数を書いたら、受け取る関数かんせーい!
const handleClick = async () => {
const url = `https://script.google.com/macros/s/AKfycbxhh8JTE_S8BwumXdm0oR4OeV1JnMN2z0Ts_EE0nslNI-_IqMbOiSaz-CmemAjt1TdP/exec?sei=${encodeURIComponent(inputText)}&mei=${encodeURIComponent(inputName)}`;
const fox_url = `https://script.google.com/macros/s/AKfycbwHTxWjNm_Xr-jVbN_iH7ZY3jwLFp443109RwTfo2tTmqMl3eWRCDIwpxAfbQTOeGxK/exec?sei=${encodeURIComponent(inputText)}&mei=${encodeURIComponent(inputName)}`;
setLoading(true);
setError(null);
try {
//データURL1つめ
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
const data = await response.text();
//データを解析する
console.log(data);
}
return{
<main>
<div>
<button
className="btn-fetch"
onClick={handleClick}>Fetch Data</button>
{loading && <p>Loading...</p>}
{error && <p>Error: {error}</p>}
{/*message && <p>天運: {message}</p>*/}
</div>
</main>
}
ここまでやってようやく技術要旨の1つ目がクリアできた。この時点で7日が経過。目標の1週間はすでに過ぎてしまっていた…(絶望)
絶望の2週目突入
さて、どうしてやろうかどうしてやろうか。もうここまできたら作るしかねえよなぁ?!
ここからは受け取ったコードの整形だ。生データではHTMLのタグの残骸が残っている。ここからデータを抽出して整形する必要がある。以下のサイトやコードを参考にしてデータの整形を行った。
//選択した文章から指定したワード(from,to)の前後を切り取る
function findTag(text, from, to) {
var fromIndex = text.indexOf(from);
var er_from = "can't found from word";
if (fromIndex === -1) return console.log(er_from);
text = text.substring(fromIndex + from.length);
var toIndex = text.indexOf(to);
var er_to = "can't found toto word";
if (toIndex === -1) return console.log(er_to);
return text.substring(0, toIndex);
}
//選択した文章から指定したワード(from)の前を切り取る
function cutFrom(text, from) {
var fromIndex = text.indexOf(from);
var er_from = "can't found from word";
if (fromIndex === -1) {
console.log(er_from);
return '';
}
return text.substring(fromIndex + from.length);
}
//選択した文章から指定したワード(to)の後を切り取る
function cutTo(text, to) {
var toIndex = text.indexOf(to);
var er_to = "can't found to word";
if (toIndex === -1) {
console.log(er_to);
return '';
}
return text.substring(0, toIndex);
}
思わぬ伏兵
でも、なぜか無限に\nが消えない。他の文字やタグは消えるのに何をしても\nだけ消えない。ぴえん。
うだうだやってたら1日経過。正解は「VSCodeではなくGASのバックエンドサーバー側で\nを置き換え処理して消す」でした〜!クソ!!!!!!!
ここからは何も面白くないです。
ただひたすらロジックをJSに直し、精神統一して心を落ち着け、台パンしながら計算処理を記述していくだけ。ここで4日経過()
そして一番最後の大トリは表の表示だ。
ラスボス(衝撃)
私は、最初に表を作成する際の技術要旨に「forループを回して二次元配列表示すればいいんやな」と書いた。えーっとJavaScriptで二次元配列を作成する方法は…?
JavaScriptだと多次元配列を直接宣言できない
JavaScriptに二次元配列はありません
?! どういうことだってばよ…? 俺らの二次元配列がないだって?? 話が違うじゃねえかマイブラザー…
そう、JavaScriptには二次元配列がない。
一次元配列を配列の中に入れることで擬似的に2時配列を再現することはできるが…うーむ。
その際、一次元配列を配列の中に入れる時にJSならではの注意点がある。
それは、「配列の宣言」だ。
calc_rokusei = function(birthdate) {
const year = parseInt(birthdate.substring(0, 4), 10);
const currentYear = new Date().getFullYear();
//単配列の宣言
var array = [];
var kari_year=year;
var fate_list = calc_divination2(birthdate);
//for文で要素を格納する
for(var i=0; i<(currentYear-year+12); i++){
if (i == 0) {
array[i] = ["年号", " 表運", " 裏運"];
} else {
//ここで配列を宣言してから入れないとエラーが出る
array[i] = [];
for (var j = 0; j < 3; j++) { // ここは3に変更、カラムは3つです
if (j == 0) {
array[i][j] = kari_year + "("+ (kari_year-year) +")";
kari_year = kari_year + 1;
}
else if(j == 1){
//array[i][j] = dict_fate[(star_num+i)%12];
array[i][j] = " " + dict_fate[(fate_list[3]+i-1)%12];
}
else if(j == 2){
array[i][j] = " ( " + dict_fate[(fate_list[3]+i-1+6)%12] + " )";
}
}
}
}
return array ;
}
配列の中に行の代わりにたくさんの配列を入れる。
だからforループで回す最中に、その都度新しい配列を宣言する必要がある。
宣言無しだと「入れようとしてる配列、そんなもんないで」っていうふうにエラーで弾かれる。これに気づくのにまじで半日以上使った。
アプリの完成
苦節20日、アプリのかんせいだ!!!やっ!!!!!た!!!!
解放!!!!!された!!!!!!
完成図


今回のリクレクション
皆様は、このページの最初で私がたてた世にも愚かなクソガバ技術要旨を覚えているだろうか。今回の結果を踏まえて前述のクソガバ技術要旨を修正していこうと思う。
Webサイトから占いのデータをGetする
→バックエンドからGetしてデータをapiで飛ばす。
→POSTMANでリクエスト飛ばせてるか確認(重要)
→GASはきちんとデプロイ更新しようバックエンドで入力データを計算
→JSにロジックを書き直す。気合。計算済みデータを表示できる形に整形
→基本replaceメソッド+正規表現を組み合わせて整理する。
→なんか\nとか消えないやつはGAS側で処理しようね持ってきたデータを画面に表示
→useState関数を用い、set〇〇で代入する
→配列を定めた場合はまとめて更新しよう表を作って画面に表示
→二次元配列なんてない()
次回予告
次は複数ページのあるアプリを作ろうかな…(震え声)
その場合、React以外に手を出さなければいけなくなる…?
次回もお楽しみに!
以上、めっどべいでした(泣)