ReactアプリのPWA化（Webアプリ化）手順

個人メモ

## 前提
+ 新規で作る場合の手順
+ 以下のイメージはVSCodeを使用
+ Reactを使用（Reactでテンプレートからサンプルプログラムを作れる程度の環境構築済み）
+ 開発環境でのテスト実行までを記載、実デプロイ等は含まれていません

## 手順
+ 手順その１　プロジェクトの作成
コマンドラインにて下記を入力
`> npx create-react-app [プロジェクト名] --template cra-template-pwa-typescript`
:::note warn
TypeScriptでの記述を想定
:::

+ 手順その２　PWAの有効化
[プロジェクト名]/src/index.tsxを書き換え

```diff_tsx:index.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import * as serviceWorkerRegistration from "./serviceWorkerRegistration";
import reportWebVitals from "./reportWebVitals";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
- serviceWorkerRegistration.unregister();
+ serviceWorkerRegistration.register();

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
```

下記画像のようにアドレスバー右端のアイコンをクリックすると、
「アプリをインストールしますか？」のプロンプトが出ればＯＫ（Chromeの場合）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/427bd52f-29ce-a1bb-6b85-7f3aa8227aaa.png)


+ 手順その３　アプリの作りこみ
好きなようにアプリを作りこんでください。
以降は、テンプレートのまま進めた場合になります。
<br/>

+ 手順その４　インストールボタンの実装
[プロジェクト名]/src/installButton.tsxを新規追加する

```ts:installButton.tsx
import { useEffect, useState } from "react";

interface IBeforeInstallPromptEvent extends Event {
  readonly platforms: string[];
  readonly userChoice: Promise<{
    outcome: "accepted" | "dismissed";
    platform: string;
  }>;
  prompt(): Promise<void>;
}

export const PwaInstallButton = () => {
  const [prompt, setPrompt] = useState<IBeforeInstallPromptEvent | null>(null);
  const [installed, setInstalled] = useState<boolean>(false);

  useEffect(() => {
    const event = (e: IBeforeInstallPromptEvent) => {
      e.preventDefault();
      setPrompt(e);
    };

    window.addEventListener("beforeinstallprompt", event as any);

    return () => {
      window.removeEventListener("beforeinstallprompt", event as any);
    };
  }, []);

  useEffect(() => {
    const event = () => {
      setInstalled(true);
    };

    window.addEventListener("appinstalled", event as any);

    return () => {
      window.removeEventListener("appinstalled", event as any);
    };
  }, []);

  const handleClikcInstall = () => {
    if (prompt) return prompt.prompt();
    return Promise.reject(
      new Error(
        'Tried installing before browser sent "beforeinstallprompt" event'
      )
    );
  };

  return (
    <button disabled={!prompt} onClick={handleClikcInstall}>
      {installed ? "Install済みです" : "Installする"}
    </button>
  );
};
```
:::note warn
実際にはbutton部分をMaterial-UIの部品で置き換えて使用する
:::

画面に上記ボタンを追加する（今回は画面上部）
[プロジェクト名]/src/App.tsx

```diff_tsx:App.tsx
import React from "react";
import logo from "./logo.svg";
import "./App.css";
+import { PwaInstallButton } from "./installButton";

function App() {
  return (
    <div className="App">
      <header className="App-header">
+        <PwaInstallButton />
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;
```
下記画像のように「Installする」ボタンが表示され、
クリックすると「アプリをインストールしますか？」プロンプトが表示されればＯＫ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/c22ceddf-f986-71d7-6c62-b8c79fb14471.png)

「インストールする」をクリックすると画面がアプリ化され
ボタンは「Install済みです」となる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/58a0f71b-dee2-f2f7-2da7-f886148cc9ac.png)

アンインストールは、「︙」をクリックし、
「Create React App Sample」をアンインストール...をクリックすればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/4667a622-bad1-4ddd-441c-9723cc93c215.png)

「Chrome（http://localhost:3000）からもデータを削除する」に
チェックを入れること（じゃないとショートカットだけが削除される）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/d172b744-492f-fed7-de85-6ccfe82ea2d8.png)