ローカルでアプリ作ったけど、どうやって公開するかわからない。
useMemoやらuseCallbackがでてきたけど、あれはなんだい?
WebAPIを導入してみたいけど、、
本記事では、上記のあたりについて解説いたします。
現在僕は、Shopify Exportsの企業にエンジニアとして勤務しながら、RaiseTechにてフロントエンドコースを受講しております。
今後、会社でもReact
を使った開発をしていく予定があり、また個人でもアプリ開発をしてみたいなと思っております。
自分の学習の記録でもありますが、読んでいただいた方の参考になるよう書いておりますのでぜひ読んでいってください。
本記事は、Raise Techのフロントエンドエンジニアコースの受講記でもあります。
こちらの記事は、第8回の講義のまとめとなります。
それでは、いきましょー!
デプロイについて
そもそもデプロイとはなんなのかというと、サーバー上にプログラムを配置しユーザーが使える状態にすることです。
React
でローカルでの開発をしたけど、世界中に公開したいってときにデプロイをする必要があります。
デプロイの方法が複数あるのですが、現在主流のクラウドホスティングサービスを紹介しておきます。
・Vercel
・Heroku
・GithubPages
・Netrify
・Amplify
実際に、Vercelを使って公開までしていきたいと思います。
Githubのリポジトリからアプリを公開する方法について解説になりますので、Githubにリポジトリを作成していない方は事前にリポジトリを作成しておきましょう。
Vercel
公式サイトにアクセスして、登録します。
Githubのアカウントを使って登録で問題ありません。
New Projectから新しいプロジェクトを作成します。
インポートするリポジトリを選択します。
チームは不要だと思うので、一旦スキップします。
このあたりも、一旦初期設定で問題ないかと思います。
最後にデプロイを実行します。
Congratulations!と無事にでたので、デプロイ完了しました。
めっちゃ簡単!!
実際にデプロイした先のURLがこちらになります。
https://react-osyare-todoapp-liard.vercel.app/
非常に簡単にできて、しかも無料なので安心して試すことができますね!
Reactのレンダリングへの理解を深める
まず、そもそもレンダリングというのを復習しておく必要します。
レンダリングとは、state(props)変更前の仮想DOMと変更後の差分を比較して、差分を検知することで仮想DOMを再構築することをいいます。
このレンダリングが、再び起こる条件が下記になります。
再レンダリングが起こる3つの条件
・Stateが更新されたとき
・Propsが更新されたとき
・親コンポーネントが再レンダリングされたとき
stateが更新されたとき
import { useState } from "react";
import "./styles.css";
export default function App() {
console.log("更新されるよー!!");
const [text, setText] = useState("");
const changeText = (e) => {
setText(e.target.value);
};
return (
<div className="App">
<h1>stateが更新されたとき</h1>
<input type="text" onChange={changeText} />
</div>
);
}
インプット欄にテキストを入力するたびに、コンソールに更新されるよー!!
と表示がされます。
このことから、state
が更新されるたびに再レンダリングが発生していることが分かるかと思います。
propsが更新されたとき
import { Child } from "./components/Child";
import { useState } from "react";
import "./styles.css";
export default function App() {
const [count, setCount] = useState(0);
const countUp = () => {
setCount(count + 1);
};
return (
<div className="App">
<h1>propsが更新されたとき</h1>
<button onClick={countUp}>カウントアップ</button>
<Child count={count} />
</div>
);
}
export const Child = (props) => {
console.log("propsが更新されたとき");
const { count } = props;
return (
<>
<h2>子要素</h2>
<p>{count}</p>
</>
);
};
親のAppコンポーネントから子のChildコンポーネントにcount
というprops
を渡しています。
カウントアップボタンを押すたびに、コンソールにpropsが更新されたとき
が表示されます。
このことから、props
が変更されたときに再レンダリングが発生しているのが分かるかと思います。
親コンポーネントが再レンダリングされたとき
import React, { useState } from "react";
import { Child } from "./Child";
export const Parent = () => {
const [num, setNum] = useState(0);
const onClick = () => {
setNum((num) => num + 1);
};
console.log("親要素を再レンダリング");
return (
<div>
<button type="button" onClick={onClick}>
COUNT UP!!!!!!!
</button>
<br />
{num}
<Child />
</div>
);
};
export const Child = () => {
console.log("子要素を再レンダリング");
return <div>子要素</div>;
};
上記のコードでは、親子間でのpropsの受け渡しはないが、親コンポーネントでカウントアップがされるたびに、子コンポーネントの子要素を再レンダリング
のコンソールが表示されます。
このことから、親コンポーネントが再レンダリングされたタイミングで子コンポーネントも再レンダリングされることが分かります。
これだと何がダメなのかな??
小さなアプリであれば問題はないのですが、大きなアプリや重たい処理をする必要がある場合には、動き遅くなってしまうのです。
この再レンダリングが起こる条件だと、不要な再レンダリングが起こってしまいます。
それを最適化するために、Reactでは以下の3つの機能が用意されております。
・memo
・useCallback
・useMemo
これらの機能を使って、メモ化(計算結果を保持し、それを再利用する)ことができます。
1度実行済みの処理については、再度行う必要ないですよね。
メモ化をしておくことで、前回の処理結果を呼び出して使用することができるようになります。
memo
import React, { useState } from "react";
import { Child } from "./Child";
export const Parent = () => {
const [num, setNum] = useState(0);
const onClick = () => {
setNum((num) => num + 1);
};
console.log("親要素を再レンダリング");
return (
<div>
<button type="button" onClick={onClick}>
COUNT UP!!!!!!!
</button>
<br />
{num}
<Child />
</div>
);
};
import { memo } from "react";
export const Child = memo(() => {
console.log("子要素を再レンダリング");
return <div>子要素</div>;
});
メモ化する前には、子コンポーネントも再レンダリングされて子要素を再レンダリング
が表示されてしまっていたのが、メモ化したことでコンソールに表示がされなくなりました。
useCallback
こちらは関数をメモ化する際に使用します。
第1引数に実際の関数を、第2引数に、依存する変数を配列で指定します。
ここで指定された変数に変更がない場合には、この関数は実行されなくなる仕組みになっています。
▼参考記事
https://tyotto-good.com/blog/usecallback
useMemo
useMemoイマイチ理解できていないので、、、、パス!!!
非同期処理について
非同期処理は今までめちゃくちゃ避けていた(いや、あまり必要でなかった)のですが、せっかく講義で学んだので簡単な内容のみ自分なりにまとめてみました。
いずれ、実務で使うようになってから実践的な内容についてはまとめていければと思っています。
まず、非同期処理について簡単にまとめますと、
そして、この処理を扱うための技術の進化は非常に早くここが僕含め初学者が混乱してしまう箇所だと勝手に思っている。
先に非同期処理の進化の流れについて説明をして、そのあと実際のコードを見ていきます。
- ES5ではprototypeを使う
- ES6ではPromiseを使う
- ES7ではasync, awaitを使う
Promiseとかasyncとかawaitとかって一緒の並びにいるかと思ったけど、
最新の書き方だけを考えればasync/awaitだけでいいのね。
このあたりの用語のせいで
「うっ」
となってしまっていたのですが、僕も歴史を学んで少し理解できたので見ていきましょうー!
まず、非同期処理が導入される前のプログラムについて解説します。
先程も言ったとおりプログラムは書いた順番に処理されますので、例えばYoutubeのサイトを表示させたさせたいときに下記のようなコードを書いたとします。
task1();
task2();
task1が非常に長い作業だったとすると、task1()が完了するまでtask2()は実行することができません。
非同期処理を行うことで、task1が完了する前にtask2を実行できるようになります。
非同期処理が実装されている例。URLを入力したときに、動画のデータを取得しにいってます。動画データを取得しに行っている間に、他のhtmlを表示されているのが分かるかと思います。
データ取得前に一部表示されているのが分かるかと思います。
Prototypeでの実装
setTimeout(() => console.log(1), 6000);
setTimeout(() => console.log(2), 3000);
console.log(3);
通常のプログラムだと上から処理がされるのでコンソールには、1 -> 2 -> 3
の順番で表示されると思いますが、ここではsetTimeout関数
を使うことで、非同期処理を実現させています。
setTiemout関数
ですは、第1引数に関数を記述、第2引数には指定した時間を記述することで第1引数の関数の処理するタイミングを指定することができます。
なので上記のコードでのコンソールの結果は、下記のようになります。
これで非同期処理はもうOKといきたいところなのですが、setTimeout関数
だと非同期処理を順番に実行したいときに処理が複雑になってしまいます。
setTimeout(() => console.log(3), 1000);
setTimeout(() => console.log(2), 1000);
setTimeout(() => console.log(1), 1000);
上のコードだと1秒後に全て同じタイミングで3->2->1
がコンソールに表示されています。これを順番に1秒ずつ表示させようとしたときにコールバック地獄と呼ばれる状態になってしまします。
setTimeout(() => {
console.log(3);
setTimeout(() => {
console.log(2);
setTimeout(() => {
console.log(1);
}, 1000);
}, 1000);
}, 1000);
確かにこれは見づらい、、、
この課題(問題)を解決するために誕生したのが、Promiseという概念になります。
Promise
Promiseを用いることで連続した非同期処理をフラットに書くことができるようになります。さっきのネストで書いていたようなコードを書く必要がなくなるんですね。
new Promise((resolve) => {
setTimeout(() => {
console.log(3);
resolve();
}, 1000);
})
.then(() => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(2);
resolve();
}, 1000);
});
})
.then(() => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(1);
resolve();
}, 1000);
});
});
最初はnew Promise()をすることで、待たせる状態にすることができます。
処理が完了されたら、resolve()が実行されて完了した状態になります。
完了すると、thenで繋げて次に処理に移ることができるようになります。
ネストがなくなったのね。
けど、まだコードが長すぎる。
async/await
ネストが解消されたのですが、まだコードが長く見にくいということで生まれたのがasync/awaitになります。
const func = async () => {
await log(3);
await log(2);
await log(1);
await log("ダ――――――!!");
};
const log = (num) => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(num);
resolve();
}, 1000);
});
};
func();
だいぶコードがスッキリしました。
ポイントは、async
とawait
です。async
を関数の前に記述するだけで非同期処理を実現することができるようになります。
async
がついている関数はPromise
を返すようになります。また、await
はPromise処理
の結果が返ってくるまで一時停止してくれる演算子となります。
だいぶざっくりの解説ですが、いったんここまでにしておきましょう。
時間見つけてより詳しい解説を別記事でまとめていきますね。
WebAPIに触れてみる
Web制作だけをやっていたときはAPIって効いても全く分からなかったのですが、本業でエンジニアなってからやたらAPIがうんたらかんたらって会話を聞くようになりました。
APIとは、アプリケーションとアプリケーションの機能を共有するためのものになります。
(WebAPIも同じものだと思ってOK…なはず。)
サービスを提供している会社が、他のアプリケーションと機能を共有するためにAPIを公開してくれています。
では、実際にWebAPIを触ってみようと思います。
今回は、天気予報を取得して表示したいと思います。クジラ週間天気APIからデータを取得しようと思います。
エンドポイントはこちらになります。
https://api.aoikujira.com/tenki/week.php?fmt=json&city=319
上のリンクにアクセスすると、json形式のファイルが見ることができます。
ここのURLからデータを取得して、画面に表示させたいと思います。
import "./styles.css";
import axios from "axios";
import { useState, useEffect } from "react";
export default function App() {
const [weekWeather, setweekWeather] = useState([]);
useEffect(() => {
axios
.get("https://api.aoikujira.com/tenki/week.php?fmt=json&city=319")
.then((res) => {
setweekWeather(res.data[319]);
});
}, []);
return (
<div className="App">
<h1>東京の1週間の天気です。</h1>
<h2>
{weekWeather.map((obj, index) => (
<>
<ul key={index} className="forecast__list">
<li>
<div className="date">{obj.date}</div>
<div className="tenki">{obj.forecast}</div>
</li>
</ul>
</>
))}
</h2>
</div>
);
}
axiosというフロントエンドとAPI間の通信ライブラリを使用して実装しております。
getにデータを取得しにいくURLを記述します。.get("https://api.aoikujira.com/tenki/week.php?fmt=json&city=319")
thenには取得したデータがresで返ってきているので、更新関数にresのデータをセットします。
そうすることで、データを扱うことができるようになります。
あとはreturnのなかで配列を展開すると、正しく表示されるのが分かるかと思います。
WebAPIについてなので細かい文法の話は説明しませぬ、、、。
WebAPIを使えるとなんとなくエンジニアっぽくてかっこいいですよね。
まとめ
これからReact開発をしていくにあたって、パフォーマンスをよくするための技術は抑えておく必要があるなぁと思いました。
あとは、APIを叩くのが楽しい。亀ペースですが、エンジニアリング楽しんでます〜!