デプロイ、レンダリング理解、非同期処理、WebAPI【RaiseTech フロントコース#8】

悩んでいる人

ローカルでアプリ作ったけど、どうやって公開するかわからない。

悩んでいる人

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が更新されるたびに再レンダリングが発生していることが分かるかと思います。

CodeSandbox
raisetech08_stateが更新されたとき - CodeSandbox raisetech08_stateが更新されたとき by takahiro-okada using react, react-dom, react-scripts

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が変更されたときに再レンダリングが発生しているのが分かるかと思います。

CodeSandbox
raisetech08_propsが更新されたとき - CodeSandbox raisetech08_propsが更新されたとき by takahiro-okada using react, react-dom, react-scripts

親コンポーネントが再レンダリングされたとき

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の受け渡しはないが、親コンポーネントでカウントアップがされるたびに、子コンポーネントの子要素を再レンダリングのコンソールが表示されます。

このことから、親コンポーネントが再レンダリングされたタイミングで子コンポーネントも再レンダリングされることが分かります。

CodeSandbox
raisetech08_親コンポーネントが再レンダリングされたとき - CodeSandbox raisetech08_親コンポーネントが再レンダリングされたとき by takahiro-okada using react, react-dom, react-scripts
考える人

これだと何がダメなのかな??

オカ

小さなアプリであれば問題はないのですが、大きなアプリや重たい処理をする必要がある場合には、動き遅くなってしまうのです。

この再レンダリングが起こる条件だと、不要な再レンダリングが起こってしまいます。

それを最適化するために、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>;
});

メモ化する前には、子コンポーネントも再レンダリングされて子要素を再レンダリングが表示されてしまっていたのが、メモ化したことでコンソールに表示がされなくなりました。

CodeSandbox
raisetech08_memo - CodeSandbox raisetech08_memo by takahiro-okada using react, react-dom, react-scripts

useCallback

こちらは関数をメモ化する際に使用します。

第1引数に実際の関数を、第2引数に、依存する変数を配列で指定します。

ここで指定された変数に変更がない場合には、この関数は実行されなくなる仕組みになっています。

▼参考記事

https://tyotto-good.com/blog/usecallback

CodeSandbox
raisetech08_useCallback - CodeSandbox raisetech08_useCallback by takahiro-okada using react, react-dom, react-scripts

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();

だいぶコードがスッキリしました。

ポイントは、asyncawaitです。asyncを関数の前に記述するだけで非同期処理を実現することができるようになります。

asyncがついている関数はPromiseを返すようになります。また、awaitPromise処理の結果が返ってくるまで一時停止してくれる演算子となります。

オカ

だいぶざっくりの解説ですが、いったんここまでにしておきましょう。
時間見つけてより詳しい解説を別記事でまとめていきますね。

WebAPIに触れてみる

Web制作だけをやっていたときはAPIって効いても全く分からなかったのですが、本業でエンジニアなってからやたらAPIがうんたらかんたらって会話を聞くようになりました。

APIとは、アプリケーションとアプリケーションの機能を共有するためのものになります。
(WebAPIも同じものだと思ってOK…なはず。)

サービスを提供している会社が、他のアプリケーションと機能を共有するためにAPIを公開してくれています。

侍エンジニアブログ
【2024年最新】作りたいアプリ別API一覧を全紹介〜随時更新〜 | 侍エンジニアブログ この記事では「 【2024年最新】作りたいアプリ別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を使えるとなんとなくエンジニアっぽくてかっこいいですよね。

CodeSandbox
raisetech08_API_東京の天気 - CodeSandbox raisetech08_API_東京の天気 by takahiro-okada using axios, react, react-dom, react-scripts

まとめ

これからReact開発をしていくにあたって、パフォーマンスをよくするための技術は抑えておく必要があるなぁと思いました。

あとは、APIを叩くのが楽しい。亀ペースですが、エンジニアリング楽しんでます〜!

スポンサーリンク

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
目次