MRが楽しい

MRやVRについて学習したことを書き残す

TypeScript+React+Electronでデスクトップアプリを開発する その2(コードを作成する)

本日はTypeScriptの学習枠です。
TypeScript+React+Electronでデスクトップアプリを開発する手順を試してみました。
今回はコードの作成です。

前回記事

以下の前回記事の続きです。
bluebirdofoz.hatenablog.com

コードを作成する

アプリのコードを置くフォルダを用意します。
src以下にElectronのメインプロセス用main、Reactのレンダラープロセス用rendererを分けてフォルダを作成しました。

次はElectronのメインプロセス用エントリーファイル(main.ts)を用意します。
メインプロセスはアプリ起動時に実行され、ウィンドウの生成などデスクトップアプリ全体の制御を担当します。

main.tsはElectronのメインプロセスで実行され、アプリ全体のライフサイクル・ネイティブ機能・レンダラープロセス起動を統括する役割を持ちます。

・main.ts

import { app, BrowserWindow } from 'electron';

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: undefined,
    },
  });

  if (process.env.ELECTRON_START_URL) {
    win.loadURL(process.env.ELECTRON_START_URL);
  } else {
    win.loadFile('dist/renderer/index.html');
  }
}

app.whenReady().then(() => {
  createWindow();

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow();
    }
  });
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

次にレンダラープロセス側の土台を作ります。初めにブラウザ画面のベースになるindex.htmlを用意します。
このファイルはReactの描画先となるdiv#rootを持ち、ビルド後のスクリプトを読み込みます。

div#root は HTML 側で React の描画結果を差し込む“受け皿”です。

ブラウザーは起動直後は通常の HTML を表示します。このとき index.html だけが読み込まれていて、まだ React は動いていません。
React アプリを起動すると ReactDOM.createRoot(document.getElementById('root')!) のようにして、特定の DOM 要素を取得し、その内部にコンポーネントの描画結果をレンダリングします。
そのため HTML にあらかじめ <div id="root"></div> を置いておく必要があります。これがないと、React が描画を差し込む先を見つけられません。
実行後は、この div の中身だけが React によって動的に更新される仕組みになっています。

・index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <title>Hello Electron React</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="./index.js"></script>
  </body>
</html>

次はReactコンポーネント本体を作ります。
src\renderer\App.tsxではTypeScript + JSXで画面を記述し、「Hello World」を表示するシンプルなコンポーネントにしました。

App.tsx は React アプリの“中身”を定義するファイルです。

React では画面をコンポーネント(部品)として記述します。App.tsx はその最上位コンポーネントとして、アプリ全体の表示内容をまとめる役割を持ちます。
TypeScript の拡張子 .tsx は TypeScript と JSX(JS の中で HTML ライクな構文を書く仕組み)を同時に書けるファイル形式です。React.FC 型を使うことで、関数型コンポーネントであることを TypeScript に伝えています。
今回は <h1>Hello World</h1> を返しているだけですが、今後はここに他のコンポーネントを組み込んだり、状態管理やイベント処理などを追加して複雑な UI を構成していきます。
別ファイルで App.tsx を import し、ReactDOM がレンダリングすると、App コンポーネントが返す JSX の内容が実際の DOM に描画されます。

・App.tsx

import React from 'react';

const App: React.FC = () => {
  return <h1>Hello World</h1>;
};

export default App;

次にReactアプリをブラウザーに差し込むエントリーポイントを作ります。
src\renderer\index.tsxは次の役割を持ちます。

createRoot で div#root を取得し、React が描画を担う領域を確保します。
App コンポーネントを実際にレンダリングする起点になります。
レンダラーのビルド結果(index.js)はこのファイルから生成され、index.html に読み込まれます。

・index.tsx

import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('root');
if (!container) {
  throw new Error('Root element not found');
}

const root = createRoot(container);
root.render(<App />);

次回はアプリケーションのビルドと実行を行います。