Electron ForgeでReactを使うための基礎づくり

:::note info
**Info**
この記事は2023/02/25現在のものです。バージョン変更により方法が変わることがあります。
:::

# はじめに

## Electronとは

ElectronはWebサイトの言語（JavaScript、CSS、HTML）を利用して、マルチプラットフォームアプリを開発することができるフレームワークです。JavaScriptの知識さえあれば、C++やC#などで作るネイティブアプリの知識がなくても、簡単にデスクトップアプリを作ることができます。

https://www.electronjs.org/ja/docs/latest/

## Electron Forgeとは

Electron ForgeはElectonアプリケーションをパッケージ化・配布するためのオールインワンツールで、コード署名などといったビルド処理をまとめてくれます。

https://www.electronforge.io/

# Electron Forgeのテンプレートを作る

Electron Forgeのテンプレートを作ります。
WebpackとTypeScriptに対応させるため、以下のコマンドを利用します。

```console
$ npm init electron-app@latest <アプリ名> -- --tempate=webpack-typescript
```

完了するとアプリ名のディレクトリが現在のディレクトリ内に作成されます。そのディレクトリを使用しているエディタで開きます。

# 一旦動くかチェック

パッケージスクリプト`start`でElectron Forgeのアプリが起動するかを確認します。

```console
$ npm start
```

以下のようになれば、OKです。WindowsやLinuxの場合はウィンドウを閉じる、macOSの場合はメニューから"Quit"や「終了」を選択すればアプリは終了します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111880/8199b1d3-20ff-194f-c27d-b634ce10749e.png)

# Reactを導入

Electron ForgeアプリにReactを対応させるため、以下の手順を実行します。

## npmパッケージの導入

npmパッケージの`react`と`react-dom`を導入します。

```console
$ npm install react react-dom
```

TypeScriptに対応させるため、`@types/react`と`@types/react-dom`を導入します。

```console
$ npm install --save-dev @types/react @types/react-dom
```

## `tsconfig.json`の変更

TypeScriptにJSX(TSX)を対応させるため、`tsconfig.json`を開き、`"jsx": "react-jsx"`を追加します。

```diff_json:tsconfig.json
{
  "compilerOptions": {
    "target": "ES6",
    "allowJs": true,
    "module": "commonjs",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "noImplicitAny": true,
    "sourceMap": true,
    "baseUrl": ".",
    "outDir": "dist",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "paths": {
      "*": ["node_modules/*"]
-   }
+   },
+   "jsx": "react-jsx" 
  },
  "include": ["src/**/*"]
}
```

## `index.html`の変更

Reactがレンダリングするための要素を追記します。
最初に定義されている`h1`タグと`p`タグは不要なので削除します。
`title`タグは適宜変更しても構いません。

```diff_html:src/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello World!</title>

  </head>
  <body>
-   <h1>💖 Hello World!</h1>
-   <p>Welcome to your Electron application.</p>
+   <div id="app"></div>
  </body>
</html>
```

## `App.tsx`の作成

Reactコンポーネントのメイン部分になる`App.tsx`を作成します。

```tsx:src/App.tsx
import { useState } from "react";

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <h1>Hello world from React</h1>
      <p>with Electron Forge!</p>
      <p>You clicked {count} time(s)</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </>
  )
}

export default App;
```

## `root.tsx`の作成

Reactの大元となる`root.tsx`を作成します。

```tsx:src/root.tsx
import { createRoot } from "react-dom/client";

import App from "./app";

function render() {
  const root = createRoot(document.getElementById("app"));
  root.render(<App />);
}

render();
```

## `renderer.ts`の変更

先程作成した`root.tsx`を、`renderer.ts`にインポートさせます。
同時に、ログを出力するスクリプトを削除しておきます。

```diff_typescript:src/renderer.ts
// -- snip --

import './index.css';
+ import './root';

- console.log('👋 This message is being logged by "renderer.js", included via webpack');
```

# 動作チェック

最初にチェックしたときと同様の手順で、動作チェックします。

```console
$ npm start
```

以下のようになっていれば動作OKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111880/02b6ae81-fc2e-0408-5a68-bbba05d1f0e9.png)

「Click me」をクリックすると、数字がカウントアップされることを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111880/85c02913-c29f-fcd0-e96d-66c9a748ca65.png)

# React DevToolsを導入する

:::note alert
2023/02/25現在、**React DevToolsは動作しません。**
React DevToolsのマニフェストファイル絡みの問題が発生しているためと言われています。
詳しくは以下のGitHub Issuesをご確認ください。(英語)
- https://github.com/electron/electron/issues/36545
- https://github.com/facebook/react/issues/25843
:::

Reactの開発を円滑に行うための拡張機能、React DevToolsを導入します。

## npmパッケージの導入

npmパッケージ`electron-devtools-installer`をインストールします。

```console
$ npm install --save-dev electron-devtools-installer
```

## `index.ts`の変更

React DevToolsをインストールするためのスクリプトを、`index.ts`に追記します。

```diff_typescript:src/index.ts
import { app, BrowserWindow } from 'electron';
+ import installExtension, { REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer';

// -- snip --
const createWindow = (): void => {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    height: 600,
    width: 800,
    webPreferences: {
      preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
    },
  });

+ // Install React DevTools extension
+ installExtension(REACT_DEVELOPER_TOOLS)
+   .then((name) => console.log(`Extension installed: ${name}`))
+   .catch((err) => console.error(err));

  // and load the index.html of the app.
  mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);

  // Open the DevTools.
  mainWindow.webContents.openDevTools();
};

// -- snip --
```

## チェック

開発ツールのタブに`⚛ Components`や`⚛ Profiler`があれば導入OKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111880/37c52a69-e458-024a-aff9-b2e583684aab.png)

---

今回の内容はGitHubリポジトリにテンプレートとして上げています。ご活用ください。

https://github.com/YuzuRyo61/electron-forge-react-template