簡単なReactコンポーネントのテンプレセットをちゃちゃっと作りたい

2022/12/12に公開3

こんにちは、ちょっと株式会社でフロントエンドエンジニアをしているShumpeiと申します。

今回は、「ちょっとした関数を作ってReactのコンポーネントのプリセットを作って楽をしよう」という話を書きます。

必要なもの

  • シェル(ターミナル)
  • .zshrc.bashrc などのシェルのコンフィグファイル

以上です。
どのPCにも入ってるもので作ります。

わかる人に向けては下記の関数をコンフィグファイルに書いてね、で伝わるかと思います。
簡単なシェルスクリプトです。

function raku_dayo() {
  DIR_NAME=$1
  ROOT_TAG=$2
  echo "create: index.tsx and index.module.scss."
  mkdir ${DIR_NAME} && cd ${DIR_NAME} && touch index.tsx && echo "import * as style from './index.module.scss'\nexport const ${DIR_NAME}:React.FC = () => { return (<${ROOT_TAG}></${ROOT_TAG}>) }" > index.tsx && touch index.module.scss 
}

何でやっているか、どう使うか、何をやっているか、は続きをご覧ください。

何でやっているか

突然ですが、僕はズボラです。
なので、自分の作業を楽にできる方法があればつい試したくなります。
いわゆる、30分かかる作業を3分にするため、3時間使うタイプ。

というのが前提です。

そして、今取り組んでいる案件は Next.js で、 CSS Modules を使って実装しています。
ディレクトリ構造は、仮にこのような形とします。

...
- src
  - components
    - global
      - GlobalHeader
	- index.tsx
	- index.module.scss
      - GlobalFooter
        - index.tsx
	- index.module.scss
...

ディレクトリ名がコンポーネント名を指し、 index.tsxindex.module.scss を格納します。

このような方法はvercelのボイラープレートから着想を得ています。

さて、この方針では1つのコンポーネントを作るのに、4つの作業が発生します。

  • コンポーネント名を冠したディレクトリを作成する
  • index.tsx を作成する
  • index.module.scss を作成する
  • index.tsx 内に、 index.module.scss をimportする記述を追加する

index.tsxの初期段階はこんな感じ。

import * as style from './index.module.scss'

const GlobalHeader:React.FC = () => {
  return <header>Header dayo</header>
}

これを必要なコンポーネント分作ります。
うーん、これは繰り返し作業になることが予想されますね。
正直めんどくさいです。

そこで自分で簡単にこの作業を一括で行う関数を書きました。

どう使うか

関数を登録する

vimやVSCodeなど、お使いのエディタで開いてみてください。
今回はvimで開きます。

$ vim ~/.zshrc

先程の関数を追記します。
vimは表示モードと編集モードが別れています。
まずは i キーを入力して編集モードにしてください。

function raku_dayo() {
  DIR_NAME=$1
  ROOT_TAG=$2
  echo "create: index.tsx and index.module.scss."
  mkdir ${DIR_NAME} && cd ${DIR_NAME} && touch index.tsx && echo "import * as style from './index.module.scss'\nexport const ${DIR_NAME}:React.FC = () => { return (<${ROOT_TAG}></${ROOT_TAG}>) }" > index.tsx && touch index.module.scss 
}

書き終えたら Esc キーで表示モードに戻り、
:wq と入力すると上書き保存されてターミナルに戻ってこれます。

記述するだけでは揮発してしまうので、次回ターミナルログイン時に使えなくなります。
そのため、永続化させましょう。

ターミナルに以下の記述を追加します。

$ source ~/.zhsrc

これで永続化できます。やったね。

関数を実行してみる

例えば src/components/common ディレクトリ配下に、 Modal コンポーネントを作ってみます。

ターミナルで任意のディレクトリに移動します。

$ cd src/components/common

ここで登録した関数を実行します。

$ raku_dayo Modal div

コマンドの入力順は以下のとおりです。

raku_dayoコマンド コンポーネント名 コンポーネントのルートに配置するDOM

末尾のエレメントの指定がない場合はフラグメントになります。

そうするとターミナルに $ create: index.tsx and index.module.scss. と表示されます。
そして Modal ディレクトリが生成され、その中に index.tsxindex.module.scss が生成されているかと思います。

...
- src
  - components
    - common
      - Modal
        - index.tsx
	- index.module.scss
...

index.tsxの中身は以下のとおりです。

import * as style from './index.module.scss'
export const Modal:React.FC = () => { return (<div></div>) }

あとは煮るなり焼くなりしていただければ!

何をやっているか

function raku_dayo() {
  DIR_NAME=$1
  ROOT_TAG=$2
  echo "create: index.tsx and index.module.scss."
  mkdir ${DIR_NAME} && cd ${DIR_NAME} && touch index.tsx && echo "import * as style from './index.module.scss'\nexport const ${DIR_NAME}:React.FC = () => { return (<${ROOT_TAG}></${ROOT_TAG}>) }" > index.tsx && touch index.module.scss 
}

シェルスクリプトは引数を $1$2や、 $@ などで格納することが出来ます。

...
DIR_NAME=$1
ROOT_TAG=$2
...

引数 DIR_NAME などは ${DIR_NAME} と記述すると中身を展開できます。
その上で、 && で連続して実行します。
一度に実行している関数は以下の通り。

mkdir ${DIR_NAME}
cd ${DIR_NAME}
touch index.tsx
echo "import * as style from './index.module.scss'\nexport const ${DIR_NAME}:React.FC = () => { return (<${ROOT_TAG}></${ROOT_TAG}>) }" > index.tsx
touch index.module.scss 

tsxファイルの中身をリダイレクト( > )で書き込んでいます。
改行は \n で行えます。

echo "import * as style from './index.module.scss'\nexport const ${DIR_NAME}:React.FC = () => { return (<${ROOT_TAG}></${ROOT_TAG}>) }" > index.tsx

そんな感じで変数を展開しながらディレクトリを作ったり、ファイルの中に書き込んだりしています。

まとめ

簡単なシェルスクリプトであればすぐ作れてしまうことを少しでも実感していただけたら嬉しいです。

そして、こういう小さい関数のまとまりを何個も組み合わせたり最適化していくことで、
自分の時間がどんどん増えます。時間は有限なので有意義に使いたいものです。

引き続き楽がしたいために努力します!

そして、ちょっと社では絶賛エンジニアを募集しています!
Jamstackに興味があるエンジニアさんはぜひご連絡ください〜〜
一緒に働きましょう〜〜🙌

ちょっと株式会社
https://chot-inc.com/


おまけ1

コンポーネントの中身だけですが、VSCode用のスニペットを作りました。

"rakuDayo": {
  "prefix": "raku",
    "body": [
      "import * as style from './index.module.scss'",
      "export const $1:React.FC = () => { return (<$2></$2>) }"
  ]
}

ra くらいまで入力すると呼び出してくれるので、エンターを押してください。
$1 に自動でカーソルが当たった状態で自動入力されるので入力後、
tabキー を1回叩くと $2 に移動しますので、お好きなエレメントを入力してください。


おまけ2

シェルスクリプトの引数あれこれ

変数 内容
$0 スクリプトの名前
$1, $2, ..., $9 第1引数, 第2引数, ..., 第9引数
$# 与えられた引数の数
$* 与えられたすべての引数. 引数全体が"で囲まれている
$@ 与えられたすべての引数. 引数一つ一つが"で囲まれている
$- シェルに与えられたフラグ
$? 最後に行ったコマンドの戻り値
$$ 現在のシェルのプロセス番号
$! 最後にバックグラウンドで行ったコマンドのプロセス番号
chot Inc. tech blog

Discussion

ko1nksmko1nksm

一般論として個別のツールを .zshrc や .bashrc に書くのは適切ではありません。一番の理由はメンテナンス性のためです。例えば最初のコードだと DIR_NAME が現在使っているシェルのグローバル変数となるため、他の同じ名前の変数が使われていたりしたら予期せぬ問題が発生する可能性があります。

じゃあローカル変数にしておけば良いのでは?と思うかもしれませんが、何かしらのミスで問題が起きるリスクを冒すだけの価値はないと思います。独立した別の実行ファイルにしておけばそのファイルの中でプロセスが閉じるので、その他のコードへの影響範囲を小さくすることができます。

また現在使っているシェルに依存せずにシェルスクリプトを書くことができるというメリットがあります。つまり bash や zsh やその他のシェルに乗り換えてもシェルスクリプトを書き換える必要がなくなります。

どうしてもそうしなければならない理由があれば別ですが、通常は別のファイルに分けた方が良いです(補足 シェルスクリプトに拡張子は必ずしも必要ありません)。その他いくつかの改善点を追加しています。

raku_dayo
#!/usr/bin/env bash
# 上記は bash を使用する場合の一例
set -eu

DIR_NAME=$1
ROOT_TAG=$2

echo "create: index.tsx and index.module.scss."
mkdir "$DIR_NAME"
cd "$DIR_NAME"
# 変数はダブルクォートでくくる。スペースが含まれる名前でも問題ないようにするためでこれは必須
# {} をつけるのは隣の文字とくっついて意味が変わるまたは見づらくなるときだけでよい

# 複数行のテキストをファイルに保存するならこの書き方がおすすめ
cat <<HERE > index.tsx
import * as style from './index.module.scss'
export const ${DIR_NAME}:React.FC = () => {
  return (<${ROOT_TAG}></${ROOT_TAG}>)
}
HERE

touch index.module.scss
# && で一行にすると読みづらくなる
# set -e を使えば && でつなげた場合と同じく
# エラーが起きたところで処理が中断される

改行コードはmac/Windowsで違ったりしてめんどくさいですが、ここでは置いておきます。

おそらく改行コードは LF に統一して問題ないはずです。一般的な開発ツールは Windows でも LF に対応していると思います。

記述するだけでは揮発してしまうので、次回ターミナルログイン時に使えなくなります。
そのため、永続化させましょう。

ターミナルに以下の記述を追加します。

$ source ~/.zhsrc

これで永続化できます。やったね。

これは「永続化」ではありません。もし .zshrc に書いたのであれば、次回ターミナルを起動した時に自動的に読み込まれるので使えます。source ~/.zshrc は(次回ではなく)現在のシェルに読み込むためのコードです。

ShumpeiShumpei

ご指摘いただきありがとうございます。
確認して不適切な部分に関して修正いたします。
これからも勉強を続けてよい情報を届けられるよう努めてまいります。
よろしくお願い致します。