😽

【React】爆速コーディングが捗る自作スニペットのすすめ

2023/04/10に公開

はじめに

VSCodeでのコーディング時にスニペットを使っている方はどれくらい居るのでしょうか。恐らくほとんどはプリセットに備わっているものか、適当な拡張機能をインストールしていると思われます。

自作する機能もありますが、少し前までは必要性を感じていませんでした。

  • できるだけ共通の環境に慣れておきたい
  • 多数派に合わせておきたい
  • タイピングとコーディングの能力があれば、少し構文が長くても苦痛ではない

しかし、定型的に書くコードが決まっても手間が変わらないことに面倒臭さを感じていました。
そこで考えを改めてスニペットを作り込んしたら作業効率が飛躍的に上がりました。

なぜスニペットを自作した方がいいのか

これ以降はJavaScript, TypeScript, React.jsの前提とします。
他言語の場合は当てはまらない可能性があります。

1. 拡張機能は網羅されていない

かなりダウンロードされている有名な拡張機能であっても狭いユースケースしかカバーしていないことが多いです。リポジトリで選定されたコーディングスタイルやReact.jsの設計に合わない可能性がむしろ大きいです。

例: コーディングスタイル

決まりきった処理のコードでも、スタイルは細部により驚くほどバリエーションがあります。例えばexport構文一つとっても、named/default、前置/後置といった組み合わせで分かれます。もちろんexport構文以外にもこの手の問題は山程あります。

例: React.jsの設計

Reactで書くときの設計や技術スタックは様々です。スタイリング手法ならCSSモジュールかCSS in JSか、状態管理ならReduxか自前かそれ以外のライブラリか等のパターンなど選択肢が多数あります。

2. 拡張機能はメンテナンスに追い付かない

最もトップに出てくるReact用の拡張機能は初学者なら入れたくなりそうですが、実は長らくメンテナンスされていません。

https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets

  • 最終更新は去年です。
  • Reduxがメイン・react-routerがv5だったりと、スニペットが明らかに時代遅れです。

ここまで放置されていないとしても、React界隈の技術は常に新陳代謝が激しいので追い付かない可能性は常に付きまといます。


上記に挙げた問題を気にせずストレスフリーでコーディングするには、正直なところ自作スニペットが最適です。

私が愛用する自作スニペット

例として個人的に作ったものを紹介します。プリセットのものと被るシンプルなスニペットもありますが、まさにかゆいところに手が届いて作って良かったという個人的なイチオシをお見せしようと思います。

JS一般

try-catch-finally

色々finallyまで欲しくなることが多いです。

  "try-catch-finally": {
    "scope": "typescript,typescriptreact",
    "prefix": "trycatchf",
    "body": [
      "try {",
      "  //",
      "} catch (error) {",
      "  //",
      "} finally {",
      "  //",
      "}"
    ]
  },

カスタムエラー

Errorを拡張する方法は覚えられずに毎回ググってしまうので諦めました。

  "Custom error": {
    "scope": "typescript,typescriptreact",
    "prefix": "cerr",
    "body": [
      "class $1Error extends Error {",
      "  constructor(...params: Parameters<ErrorConstructor>) {",
      "    super(...params);",
      "",
      "    // @ts-expect-error Maintains proper stack trace for where our error was thrown (only available on V8)",
      "    if (Error.captureStackTrace) {",
      "      // @ts-expect-error Maintains proper stack trace for where our error was thrown (only available on V8)",
      "      Error.captureStackTrace(this, $1Error);",
      "    }",
      "",
      "    this.name = '$1Error';",
      "  }",
      "}"
    ]
  }
}

一時停止させるやつ

一時的なデバッグ、特に通信中のインタラクションの実装でよく使います。console.logがあるので消し忘れにも気付きやすいです。

  "sleep": {
    "scope": "typescript,typescriptreact",
    "prefix": "sleep",
    "body": [
      "console.log('Sleep: $1ms') // sleep",
      "await new Promise((r) => setTimeout(r, $1))"
    ]
  },

React系

CSS Modulesのインポート

コンポーネントスコープなら、JSXとCSSのファイル名は99%合わせて問題ないでしょう。

  "import css modules": {
    "scope": "typescript,typescriptreact",
    "prefix": "impcm",
    "body": "import styles from './${TM_FILENAME_BASE}.module.css'"
  },
  "import scss modules": {
    "scope": "typescript,typescriptreact",
    "prefix": "impsm",
    "body": "import styles from './${TM_FILENAME_BASE}.module.scss'"
  },

use○○○4兄弟

私の好みはReact.useStateのように接頭辞を付けるタイプです。import文が減らせてスッキリするのが嬉しいです。

  "useState": {
    "scope": "typescript,typescriptreact",
    "prefix": "sta",
    "body": "React.useState($1)"
  },
  "useReducer": {
    "scope": "typescript,typescriptreact",
    "prefix": "red",
    "body": "React.useReducer($1)"
  },
  "useEffect": {
    "scope": "typescript,typescriptreact",
    "prefix": "eff",
    "body": ["React.useEffect(() => {", "  $1", "}, []);"]
  },
  "useRef": {
    "scope": "typescript,typescriptreact",
    "prefix": "ref",
    "body": "React.useRef($1)"
  },

オブジェクトのデバッグ

何かしらを画面に出力するとき便利で、意外と重宝しています。

  "debug object using <pre />": {
    "scope": "typescript,typescriptreact",
    "prefix": "predebug",
    "body": [
      "<pre style={{ padding: 8, border: '1px solid lightgray' }}>",
      "  {JSON.stringify($1, null, 2)}",
      "</pre>"
    ]
  },

ReactNode

DOM要素に挟まるpropsを型定義する場合はこれが無難ですが、その割にタイピングが面倒です。

  "React.ReactNode": {
    "scope": "typescript,typescriptreact",
    "prefix": "rn",
    "body": "React.ReactNode"
  },

React.FC

決定的な優劣があるとは思っていませんが、個人的にはfunction...に続けて書くスタイルを好んでいます。しかしそれでもReact.FCで型付けしたくなる稀なユースケースや、そう統一されたPJに参画される可能性があるので用意しました。

  "function component (FC, with props)": {
    "scope": "typescript,typescriptreact",
    "prefix": "fcp",
    "body": [
      "type $1Props = {",
      "  ",
      "};",
      "",
      "export const $1: React.FC<$1Props> = (props) => {",
      "  return null",
      "};"
    ]
  },

forwardRef

Atomic DesignでいうAtomsを実装するときに登場頻度が多いやつです。

 "forwardRef": {
   "scope": "typescript,typescriptreact",
   "prefix": "fwd",
   "body": [
     "const $1 = React.forwardRef<$2, $3>((props, ref) => {",
     "  const {} = props;",
     "",
     "  return",
     "});",
     "",
     "$1.displayName = '$1';"
   ]
 },
 "forwardRef (extend style)": {
   "scope": "typescript,typescriptreact",
   "prefix": "fwds",
   "body": [
     "type $1Props = React.ComponentPropsWithRef<'$2'> & {",
     "  //",
     "}",
     "",
     "const $1 = React.forwardRef<$3, $1Props>((props, ref) => {",
     "  const { className, ...restProps } = props;",
     "",
     "  return <$2 className={clsx($4className)} {...restProps} ref={ref} />",
     "});",
     "",
     "$1.displayName = '$1';"
   ]
 },

コールバック引数をrefに保存する

カスタムフックを作るときに割とよく使われる構文です。コールバックの変化で再レンダリングを発生させないための処理です。

  "useRef (saving callback)": {
    "scope": "typescript,typescriptreact",
    "prefix": "refc",
    "body": [
      "const saved$1 = $2React.useRef($3);",
      "",
      "React.useEffect(() => {",
      "  saved$1.current = $3;",
      "}, [$4]);"
    ]
  },

おわりに

少しでも作業効率を上げて1分1秒でも無駄にしないために、VSCodeでカスタムスニペットを作ってみるのはオススメです。

Discussion