クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【React】ピュアなTypeScriptモジュールを追加してみる(Reactとメタ言語の比較-6)

Reactの続きです。前回はstyled-componentsのcss helperでスタイルを継承し、CSS設定を完成させました。今回はTypeScript編。以前メタ言語スターターキットで作ったTypeScriptのモジュールをReact環境に追加してみます。処理の対象はReactの仮想DOMです。それではいきましょう!

【目次】

※参考:前回記事
【React】styled-componentsのcss helperでスタイルを継承(Reactとメタ言語の比較-5) - クモのようにコツコツと

※参考:Reactを習得するためにやったことまとめ
qiita.com

前回のおさらい

CSS設定を復活させる。styled-componentsのcss helperを使い共通スタイルを継承できた。 https://cdn-ak.f.st-hatena.com/images/fotolife/i/idr_zz/20210211/20210211145844.jpg JSXコンポーネントと同じファイルにCSS in JSでスタイルを書いてるのでEJS + Sass(SCSS)と比べてファイル数は半分になった。

また、ランダムなclass名が作られてスコープになるため、BEMのclass名は不要になった。 https://cdn-ak.f.st-hatena.com/images/fotolife/i/idr_zz/20210211/20210211180811.jpg CSS設定は別ファイルではなくheadタグの中にあるようだ。

※参考:【React】styled-componentsのcss helperでスタイルを継承(Reactとメタ言語の比較-5) - クモのようにコツコツと

「hello.js」の処理内容

現在、「modules/hello」フォルダの中にある「hello.js」というピュアなJSファイルを実行している。
https://cdn-ak.f.st-hatena.com/images/fotolife/i/idr_zz/20210211/20210211150430.jpg

「hello.js」の内容

const hello = JSON.parse(
    '{"message":{"text":"こんにちは、ふろんとえんど。","selector":".inner__text--hello"}}'
);
    
const message = { 
        text: hello.message.text, 
        selector: hello.message.selector 
};

const text = message.text;
const selector = message.selector;

document.addEventListener("DOMContentLoaded", () => {
    document.querySelector(selector).innerHTML = text;
    console.log("text-> " + text);
});

これはメタ言語スターターキットでTypeScript→JSコンパイル後のコードがベースになっている。

※参考:【メタ言語】フロントエンド開発スターターキットを作った(EJS、Sass(SCSS)、TypeScript) - クモのようにコツコツと

この処理をコンパイル前のTypeScriptモジュールの状態に再現してみる。

「hello.js」を「hello.ts」に変更

「hello.js」のファイル名を「hello.ts」に変更する。

import { message } from './message';

const text: string = message.text;
const selector: string = message.selector;

const hello = (): void => {
    document.addEventListener('DOMContentLoaded', () => {
        const dom: HTMLButtonElement = document.querySelector(selector) as HTMLButtonElement;
        dom.innerHTML = text;
        console.log('text-> ' + text);
    });
}

export { hello };

messageモジュールをインポートしてhello関数をエクスポートする。

messageモジュールからtextを読み込んでselectorのDOMに表示する、という内容。


なお、拡張子は前回の「mixin.ts」と同様、JSXを含むファイルとロジックのみのファイルを明確にするため、「.ts」で統一することにする。

※参考:TypeScript - Reactを使うプロジェクト内のTypeScriptをすべて.tsxで作るデメリットについて|teratail

「message.ts」を作成

同じフォルダに「message.ts」を作成する。

import hello from '../../data/hello.json';

const message: {[key: string]: string;} = {
    text: hello.message.text,
    selector: hello.message.selector
};

export { message };

「hello.json」をインポートして、message関数をエクスポート

このモジュールではhello.jsonから読み込んだデータをstring型付きの連想配列にしている(jsonデータはそのままだとany型になるため)。

「hello.json」を作成

次に「data」フォルダに「hello.json」作成する。 f:id:idr_zz:20210212063457j:plain

「hello.json」の内容

{
    "message": {
        "text": "こんにちは、りあくと。",
        "selector": ".hello"
    }
}

textキーは「こんにちは、りあくと。」、selectorキーは.hello

「data」フォルダはページに表示しているテキストデータ「data.json」を入れていたフォルダ。

textキーはメタ言語スターターキットの時は「こんにちは、ふろんとえんど。」だったが「こんにちは、りあくと。」に変更してみた。

selectorキーのclass名はの時はBEM記法の.inner__text--helloだったが、この環境ではBEM記法にする必要がなくなったので.helloと短くしてみた。

※参考:【メタ言語】フロントエンド開発スターターキットを作った(EJS、Sass(SCSS)、TypeScript) - クモのようにコツコツと

「data.json」のclass名も.helloに変更

「hello.json」と同じ「data」フォルダにある「data.json」を修正

        "inner": [
            // 中略
            {
            "title":"JS(文字列)",
            "text": "JSでテキストの文字列追加「<span class='hello'></span>」"
            }
        ],

ここのtextキーにもBEM記法のclass名.inner__text--helloがあったが.helloに変更した。

Innerコンポーネントのインポート部分を修正

helloモジュールをインポートしているInnerコンポーネント(Inner.tsx)のインポート部分を修正する。

修正前(「hello.js」をインポート)

import './modules/hello/hello';

JSファイルをインポートする時はこれだけで済んだ。

修正後(「hello.ts」のhelloモジュールをインポート)

import { hello } from './modules/hello/hello';

TSファイル「hello.ts」でexportしているhello関数を指定してインポートする。

そしてインポートしたhelloモジュールの関数を実行する。

// modules
hello();

この処理はメタ言語スターターキットのTypeScriptモジュールに準ずる。

※参考:【メタ言語】フロントエンド開発スターターキットを作った(EJS、Sass(SCSS)、TypeScript) - クモのようにコツコツと

ブラウザ確認

アプリをローカルで起動

$ npm start

おお、メッセージ「こんにちは、ふろんとえんど」が「こんにちは、りあくと。」になった! f:id:idr_zz:20210212063628j:plain

タグのclass名も.helloに! f:id:idr_zz:20210212063657j:plain

元のソールは#rootしかない。.helloはReactでレンダリングされた仮想DOM f:id:idr_zz:20210212063936j:plain ピュアなTypeScriptモジュールの処理がReactの仮想DOMにも適用されることがわかった!


ソース(GitHub)

※参考:GitHub - ryo-i/react-from-meta-lang

プレビュー(GitHub Pages)

※参考:Reactとメタ言語の比較

最後に

ということで、メタ言語スターターキットのTypeScriptモジュールをほぼそのままの形で追加し、問題なく動くことがわかりました!(しかも処理の対象がReactの仮想DOM)

本環境は元々React + TypeScript環境でしたが、TypeScriptは型を付けないピュアなJSも普通に動くので、これまであまり型を意識せずにコードを書いていました(たまに型推論でエラーを教えてくれる程度)。

今回、型付きTypeScriptモジュールを追加し、問題なく動いたのでReact環境でTypeScriptモジュールは両立可能とわかりました。これからはReact処理の中にもTypeScriptの型指定などの記述を加えていきたいと思います。

また、ファイルが増えるとJSXコンポーネントとそれ以外の処理ファイルがわかりにくくなってくるのでファイル名を下記で統一していこうと思います。(React機能上は全部「.tsx」、または全部「.ts」でも問題ないようです)

  • JSXコンポーネントは1文字目大文字で拡張子は「.tsx」
  • それ以外のファイルは小文字で拡張子は「.ts」

メタ言語スターターキットをReact環境に移行するシリーズは今回で終了になります。メタ言語スターターキットでやれていたモジュール分割などはReactでも実現可能とわかりました。また、モジュールがコンポーネントごとに一体化できてファイル数が少なく済むのメリットも感じました。

次回からはまた別のことに取り組んでいきます。それではまた!


※参考:Reactを習得するためにやったことまとめ
qiita.com