膝肘歪美 技術録ブログ

ぼちぼちオナシャス

【シェル】開発生産性爆上がりのエイリアス設定【zsh】

はじめに

シェルのエイリアス使っていますか?
めちゃくちゃ便利な機能で、開発生産性が爆上がりするエイリアス。
本稿では著者が使用しているエイリアスをいくつか紹介したいと思います

差分のあるファイルだけテスト、リントする

毎回全ファイルテストしたり、リントしてたりすると馬鹿にならない時間がかかるので、差分だけいい感じにやりたいですよね。
そんなときに差分があるファイル名だけ取得するコマンドがこれです。

git status -s | awk '{ print $2 }'

rubocop にかけたいときはこうです。

bundle exec rubocop $(git status -s '*.rb' | awk '{ print $2}')

このコマンド自体は最高なんですが、めちゃくちゃ長いんですよね...
そんなときにエイリアスの出番です

~/.zshrc

alias rc="bundle exec rubocop $(git status -s '*.rb' | awk '{ print $2}')"

これを定義して読み込めば、bundle exec rubocop $(git status -s '*.rb' | awk '{ print $2}')の代わりにrcを打つだけでよくなります。
便利すぎる。

コンテナ内で実行する

Docker Compose で開発していると、コンテナ内でコマンド実行する必要があります。
docker compose exec hoge bash してコンテナ内のシェルに入って作業してもいいのですが、ホストのシェルを整えていると、相対的に作業しにくく感じてしまいます。

そういう時、エイリアスの出番ですね。

~/.zshrc

alias dce="docker compose exec"

docker compose exec の代わりにdceと打つだけでよくなります。最高。

組み合わせる

上記を組み合わせる。
もう元には戻れなくなります。

~/.zshrc

alias crc="docker compose exec web bundle exec rubocop $(git status -s '*.rb' | awk '{ print $2}')"

至高のエイリアスが爆誕しました。

最終的にはこれの仲間を定義しまくることになります。

~/.zshrc

alias crc="docker compose exec web bundle exec rubocop $(git status -s '*.rb' | awk '{ print $2}')"
alias crca="docker compose exec web bundle exec rubocop -A $(git status -s '*.rb' | awk '{ print $2}')"
alias crs="docker compose exec web bundle exec rspec $(git status -s '*._spec.rb' | awk '{ print $2}')"
alias ccheck="docker compose exec front npx biome check $(git status -s '*.ts' '*.tsx' | awk '{print $2}' | sed 's/front\///g')" # モノレポなのでパスを合わせる
alias cwrite="docker compose exec front npx biome check --write $(git status -s '*.ts' '*.tsx' | awk '{print $2}' | sed 's/front\///g')" # モノレポなのでパスを合わせる

おわりに

本稿では著者が使用しているエイリアスを紹介いたしました。
特に効果の高いものに絞って紹介したのですが、ほかにも細かい奴をいっぱい定義してます。
特にgitやcd、dockerまわりですね。

記事を読んでくれたあなたも、最高のエイリアス見つけてみてください。

【Docker】compose ファイルの ports は環境変数の方がよくないですか?

はじめに

少し前から業務にて複数プロジェクトを触るようになりました。
どのプロジェクトもDockerを使用しています。
そのなかでfailed: port is already allocatedをめちゃくちゃ見ることになりました。
このエラー自体はcompose.yamlのportsを変更するだけで解決する簡単なエラーではあります。
ですが、compose.yamlに差分が出てしまってめちゃくちゃわずらわしいです...
compose.yamlを変更するためのPR出すときのめんどくささは目も当てられません。

そこで、本稿ではこのわずらわしさを低減するために全てのプロジェクトこうしたらいいんじゃねって方法を共有いたします。

compose ファイルの ports は環境変数の方がよくないですか?

本稿では compose ファイルの ports に環境変数を指定する方法を紹介します。

素直にcompose.yamlを書いたとき、下記のようになっていると思います。

services:
  hoge:
    command: サーバーを起動するコマンド --port xxxx
    ports:
      - xxxx:xxxx

failed: port is already allocatedが出たときに普通に解決するには下記のようにしますよね。

services:
  hoge:
    command: サーバーを起動するコマンド --port xxxx
    ports:
      - xxx1:xxxx

この方法は先述の通り、差分が一生出るので避けたいです。

そこで環境変数ですね。
下記のようにします。

services:
  hoge:
    command: サーバーを起動するコマンド --port xxxx
    ports:
      - ${HOGE_PORT:-xxxx}:xxxx

簡単すぎる。

HOGE_PORTが環境変数にあればその値になり、設定されていない、もしくは空の場合にはxxxxになります。
もちろん、HOGE_PORTの名前は任意です。値も任意です。

HOGE_PORTは.envに設定するのがいいでしょう。

.env

HOGE_PORT=xxx1

.env がgitignoreされてないときはgitignoreしてください

.env.sampleもあれば完璧ですね。

.env.sample

HOGE_PORT=

おわりに

compose.override.yamlがportsをいい感じにマージしてくれれば何も困らなかったのに...

簡単なのでぜひ全プロジェクトで採用してください!

ユーザー体験向上のための楽観的更新(Optimistic Update)を知る

はじめに

最近、業務でフロントエンドのリプレイスを進めています。
その中でパフォーマンスの向上についても見直しており、楽観的更新について調べる機会があったのでまとめます。

楽観的更新とは

楽観的更新(Optimistic Update)とは、ユーザーのアクションが正常に完了したと仮定して、サーバーからの応答を待たずにUIを更新する手法です。
UX向上のための手法の一つみたいですね。
一般的にある程度の時間がかかる通信、サーバーの処理、DBの更新を待たずにUIを更新するのでスムーズな操作が可能になります。

例えば、X(旧Twitter)の いいね だと、いいねした瞬間にUI上では +1 され、実際にサーバーへのリクエストは裏で走っている、ような感じです。
(実際に X のいいねが楽観的更新されているかどうかはよくわかんなかったです)

対して、サーバーからの応答を待ってから UI を更新する手法を悲観的更新(Pessimistic Update)と呼ぶようです。

楽観的更新のUIを実際に触ってみたい方は下記 SWR の Sandbox をご確認ください。 Optimistic UI – SWR

楽観的更新のメリット

メリットについては先ほども軽く触れましたが、

  • スムーズな操作感によるユーザー体験の向上

の一点に尽きるかなというところです。

楽観的更新のデメリット

一方でデメリットは数点考えられます。

  • データの不整合が発生する可能性がある
  • エラーハンドリングが複雑化する

フロントバリデーションが通ってリクエストしたが、一時的なエラーなどで実際にはデータがDBに反映されない、というのがデータの不整合が発生するパターンです。
この場合、レスポンスがエラーで返ってきて楽観的にした更新をロールバックする必要がありますが、ロールバックが完了するまで正しくないデータをユーザに表示してしまうことになります。
もちろん、データに不整合が発生するのは好ましくないので、確実にエラーハンドリングする必要があります。
これが実装によっては複雑になることが考えられます。

楽観的更新のイメージ図

ここでは、理解を深めるために楽観的更新のイメージを図示します。
例として、タスク一覧ページにて、モーダルなどでタスクを作成し、再検証でタスク一覧が再フェッチされるまでのストーリーを考えます。
X(旧Twitter)のタイムラインのようなものをイメージしています。

楽観的更新のイメージ図

楽観的更新によって、作成リクエストと一覧取得リクエスト、ボトルネックになりうる二か所の手前でUIを更新することができます。

楽観的更新がそぐわなそうなパターン

ここまで見てきた楽観的更新ですが、使えない、使うべきでないパターンがありそうです。
思いつく限り挙げてみます。

  1. データの整合性が重要な場合
    1. 金融系など、ユーザに万が一でも誤った情報を提供するわけにはいかないときです
  2. フォームの内容だけでは一覧に表示する内容に満たない場合
    1. レスポンスにしか含まれず、代替データで埋めることが難しいときです
      1. 例えば、ルームマッチングでのルームIDのようにサーバで一意の値を割り当てるとき
      2. 表示しない id や重要でないタイムスタンプなどは適当にフロントで埋めればよさそう
  3. ページネーションやフィルタリング、ソートをバックエンドで担っている場合
    1. 表示されているデータに含まれるかどうか、フロントで判断がつかないときです
  4. 依存関係が多い場合
    1. タスクでいうとラベルなど他のエンティティが多く依存しているときです

ぼちぼちありますね。
逆に言えばこれ以外のパターンは使えそうな気がします。

おわりに

今回は楽観的更新について、調査してなんとなく理解した内容を文字に起こしてみました。
React にて useOptimistic が実験的機能で提供されているなど、気になっていた部分について触れることができてよかったです。

納得できる実装ができたら SWR での実装方法についても投稿したい。

【VSCode】ショートカット tier 表【開発生産性爆上がり】

はじめに

VSCode のショートカット多くないですか???????????
これだけ多いとどのショートカットが便利でどれが使いにくいのかよくわからなくなっちゃいました...
以前にOSごとにショートカットキーをまとめた記事を書きまして、この機会に普段どのショートカットキーをどのくらい使ってるのかで tier 分けすることにしました。tier 大好きです。
この記事を読んでくださってる皆さんの参考になればと思っていますが、アンケートとったりしたわけではなくあくまで個人の感覚で tier を作成してます。
参考程度にご覧ください。
著者のVSCode歴は 5年ほどです。

概要

今回取り上げる VSCode デフォルトのショートカットは119個あります。めちゃくちゃありますね。
著者は覚えれて20個とかでしょうか。
この記事では、その119個あるショートカットを S~D+よくわからん の5段階 + 1分類に tier 分けしました。

ここではそれぞれのコマンドがどんなコマンドかについては言及しません。
モチベがあれば、tier S, A くらいは ここが便利!こう使え! みたいな別記事を書くかもです。

注意

初期ショートカットキーは2024年6月時点の下記を参照しています。
なお、この記事では下記pdf以外の初期ショートカットキーは無視します。

pdf からの抽出、markdown テーブルへの加工は LLM を利用しています。
その後、人手で調整しています。

予めご了承ください。

tier 温度感

tier の発表に移る前に、著者がどんな温度感でそれぞれの tier にショートカットキーを振り分けたのかを共有します。

tier S

  • 便利すぎる
  • 使えなくなると作業できない

tier A

  • 便利
  • 使えなくなると作業できない(過言)
  • なんで今まで知らんかったんや...

tier B

  • これくらいないとな
  • なくてもまあいいかな
  • 使ったことないけど便利かもな

tier C

  • こんなんもあんねや、ふーん
  • 機会あったら使ってみよかな
  • 多分使わんかな

tier D

  • 使いにくい...
  • もはや置き換えてる

よくわからん

  • 手元に Linux がない
  • キーボードにキーがない
  • 指が届かない
  • どの条件で発動するかわからん

ショートカット一覧

tier S

Category Description Windows Mac Linux
ナビゲーション ファイルに移動... Ctrl+P ⌘P Ctrl+P
ファイル管理 保存 Ctrl+S ⌘S Ctrl+S
マルチカーソルと選択 現在の選択のすべての出現を選択 Ctrl+Shift+L ⇧⌘L Ctrl+Shift+L
リッチ言語編集 シンボルの名前を変更 F2 F2 F2
リッチ言語編集 定義に移動 F12 F12 F12
一般 「クイックオープン、ファイルに移動…」 Ctrl+P ⌘P Ctrl+P
基本的な編集 行コメントを切り替え Ctrl+/ ⌘/ Ctrl+/
基本的な編集 行をインデント/アウトデント Ctrl+] / [ ⌘] / ⌘[ Ctrl+] / Ctrl+[
基本的な編集 行を下/上に挿入 Ctrl+Shift+Enter / Ctrl+Enter ⌘Enter / ⇧⌘Enter Ctrl+Enter / Ctrl+Shift+Enter
検索と置換 検索 Ctrl+F ⌘F Ctrl+F
検索と置換 次の検索一致に選択を追加 Ctrl+D ⌘D Ctrl+D
統合ターミナル 選択範囲をコピー Ctrl+C ⌘C Ctrl+Shift+C
表示 エクスプローラーを表示/フォーカスを切り替え Ctrl+Shift+E ⇧⌘E Ctrl+Shift+E
表示 サイドバーの表示を切り替え Ctrl+B ⌘B Ctrl+B
表示 ソース管理を表示 Ctrl+Shift+G ⌃⇧G Ctrl+Shift+G
表示 検索を表示 Ctrl+Shift+F ⇧⌘F Ctrl+Shift+F

tier A

Category Description Windows Mac Linux
エディター管理 エディターを閉じる Ctrl+F4, Ctrl+W ⌘W Ctrl+W
ファイル管理 すべて閉じる Ctrl+K Ctrl+W ⌘K ⌘W Ctrl+K Ctrl+W
ファイル管理 閉じる Ctrl+F4 ⌘W Ctrl+W
リッチ言語編集 選択範囲をフォーマット Ctrl+K Ctrl+F ⌘K ⌘F Ctrl+K Ctrl+F
一般 コマンドパレットを表示 Ctrl+Shift+P, F1 ⇧⌘P, F1 Ctrl+Shift+P, F1
一般 ユーザー設定 Ctrl+, ⌘, Ctrl+,
基本的な編集 行をカット (選択が空の場合) Ctrl+X ⌘X Ctrl+X
基本的な編集 行を下/上に移動 Alt+ ↓ / Alt + ↑ ⌥↓ / ⌥↑ Alt+ ↓ / ↑
基本的な編集 行を削除 Ctrl+Shift+K ⇧⌘K Ctrl+Shift+K
統合ターミナル アクティブなターミナルに貼り付け Ctrl+V Ctrl+Shift+V
統合ターミナル 新しいターミナルを作成 Ctrl+Shift+` ⌃⇧` Ctrl+Shift+`
表示 ズームイン/アウト Ctrl+ = / - ⌘= / ⇧⌘- Ctrl+ = / -
表示 拡張機能を表示 Ctrl+Shift+X ⇧⌘X Ctrl+Shift+X

tier B

Category Description Windows Mac Linux
エディター管理 エディターを分割 Ctrl+\ ⌘\\ Ctrl+\
エディター管理 前/次のエディターグループにフォーカス Ctrl+K Ctrl+ ←/→ ⌘K ⌘← / ⌘K ⌘→ Ctrl+K Ctrl + → / Ctrl+K Ctrl + ←
デバッグ ホバーを表示 Ctrl+K Ctrl+I ⌘K ⌘I Ctrl+K Ctrl+I
ナビゲーション シンボルに移動... Ctrl+Shift+O ⇧⌘O Ctrl+Shift+O
ナビゲーション すべてのシンボルを表示 Ctrl+T ⌘T Ctrl+T
ファイル管理 閉じたエディターを再度開く Ctrl+Shift+T ⇧⌘T Ctrl+Shift+T
マルチカーソルと選択 カーソルを上/下に挿入 Ctrl+Alt+ ↑ / ↓ ⌥⌘↑ / ↓ Shift+Alt+ ↑ / ↓
マルチカーソルと選択 カーソルを挿入 Alt+Click ⌥ + click Alt+Click
マルチカーソルと選択 現在の行を選択 Ctrl+L ⌘L Ctrl+L
マルチカーソルと選択 最後のカーソル操作を元に戻す Ctrl+U ⌘U Ctrl+U
マルチカーソルと選択 選択した各行の末尾にカーソルを挿入 Shift+Alt+I ⇧⌥I Shift+Alt+I
リッチ言語編集 ドキュメントをフォーマット Shift+Alt+F ⇧⌥F Ctrl+Shift+I
一般 キーボードショートカット Ctrl+K Ctrl+S ⌘K ⌘S Ctrl+K Ctrl+S
基本的な編集 ブロックコメントを切り替え Shift+Alt+A ⇧⌥A Ctrl+Shift+A
基本的な編集 行をコピー (選択が空の場合) Ctrl+C ⌘C Ctrl+C
基本的な編集 行をスクロールアップ/ダウン Ctrl+↑ / ↓ ⌃PgUp / ⌃PgDn Ctrl+ ↑ / ↓
基本的な編集 対応する括弧にジャンプ Ctrl+Shift+\ ⇧⌘\ Ctrl+Shift+\
基本的な編集 領域を折りたたむ/展開する Ctrl+Shift+[ / Ctrl+Shift+] ⌥⌘[ / ⌥⌘] Ctrl+Shift+ [ / ]
検索と置換 検索一致のすべての出現を選択 Alt+Enter ⌥Enter Alt+Enter
検索と置換 大文字小文字の区別/正規表現/完全一致の切り替え Alt+C / R / W
表示 Markdownプレビューをサイドで開く Ctrl+K V ⌘K V Ctrl+K V
表示 エディターレイアウトを切り替え(水平/垂直) Shift+Alt+0 ⌥⌘0 Shift+Alt+0

tier C

Category Description Windows Mac Linux
エディター管理 「1つ目、2つ目、3つ目のエディターグループにフォーカス」 Ctrl+ 1 / 2 / 3 ⌘1 / ⌘2 / ⌘3 Ctrl+ 1 / 2 / 3
エディター管理 エディターを左/右に移動 Ctrl+Shift+PgUp / PgDn ⌘K ⇧⌘← / ⌘K ⇧⌘→ Ctrl+Shift+PgUp / Ctrl+Shift+PgDn
ナビゲーション タブ移動でフォーカスを切り替え Ctrl+M ⌃⇧M Ctrl+M
ナビゲーション 行に移動... Ctrl+G ⌃G Ctrl+G
ナビゲーション 次のエラーまたは警告に移動 F8 F8 F8
ナビゲーション 前のエラーまたは警告に移動 Shift+F8 ⇧F8 Shift+F8
ナビゲーション 問題パネルを表示 Ctrl+Shift+M ⇧⌘M Ctrl+Shift+M
ファイル管理 アクティブなファイルのパスをコピー Ctrl+K P ⌘K P Ctrl+K P
ファイル管理 ファイルを開く... Ctrl+O ⌘O Ctrl+O
ファイル管理 次を開く Ctrl+Tab ⌃Tab Ctrl+Tab
ファイル管理 新しいウィンドウ/インスタンスでアクティブなファイルを表示 Ctrl+K O ⌘K O Ctrl+K O
ファイル管理 前を開く Ctrl+Shift+Tab ⌃⇧Tab Ctrl+Shift+Tab
マルチカーソルと選択 現在の単語のすべての出現を選択 Ctrl+F2 ⌘F2 Ctrl+F2
マルチカーソルと選択 列(ボックス)選択 Ctrl+Shift+Alt + (arrow key) ⇧⌥ + drag mouse Shift+Alt + drag mouse
マルチカーソルと選択 Shift+Alt + (drag mouse) ⇧⌥ + drag mouse Shift+Alt + drag mouse
リッチ言語編集 末尾の空白をトリム Ctrl+K Ctrl+X ⌘K ⌘X Ctrl+K Ctrl+X
基本的な編集 すべてのサブ領域を折りたたむ/展開する Ctrl+K Ctrl+[ / Ctrl+K Ctrl+] ⌘K ⌘[ / ⌘K ⌘] Ctrl+K Ctrl+ [ / ]
基本的な編集 すべての領域を折りたたむ/展開する Ctrl+K Ctrl+0 / Ctrl+K Ctrl+J ⌘K ⌘0 / ⌘K ⌘J Ctrl+K Ctrl+0 / Ctrl+K Ctrl+J
基本的な編集 ファイルの先頭/末尾に移動 Ctrl+Home / Ctrl+End ⌘↑ / ⌘↓ Ctrl+ Home / End
基本的な編集 ページをスクロールアップ/ダウン Alt+PgUp / PgDn ⌘PgUp /⌘PgDn Alt+ PgUp / PgDn
基本的な編集 行コメントを削除 Ctrl+K Ctrl+U ⌘K ⌘U Ctrl+K Ctrl+U
基本的な編集 行コメントを追加 Ctrl+K Ctrl+C ⌘K ⌘C Ctrl+K Ctrl+C
基本的な編集 行の先頭/末尾に移動 Home / End Home / End Home / End
基本的な編集 行を上下にコピー Shift+Alt + ↓ / ↑ ⇧⌥↑ / ⇧⌥↓
検索と置換 最後の選択を次の検索一致に移動 Ctrl+K Ctrl+D ⌘K ⌘D Ctrl+K Ctrl+D
検索と置換 置換 Ctrl+H ⌥⌘F Ctrl+H
統合ターミナル スクロールアップ/ダウン Ctrl+↑ / ↓ ⌘↑ / ↓ Ctrl+Shift+ ↑ / ↓
統合ターミナル トップ/ボトムにスクロール Ctrl+Home / End ⌘Home / End Shift+ Home / End
統合ターミナル ページをスクロールアップ/ダウン Shift+PgUp / PgDn PgUp / PgDn Shift+ PgUp / PgDn
表示 Markdownプレビューを開く Ctrl+Shift+V ⇧⌘V Ctrl+Shift+V
表示 ファイル内を置換 Ctrl+Shift+H ⇧⌘H Ctrl+Shift+H
表示 検索の詳細を切り替え Ctrl+Shift+J ⇧⌘J Ctrl+Shift+J
表示 出力パネルを表示 Ctrl+Shift+U ⇧⌘U Ctrl+K Ctrl+H

tier D

Category Description Windows Mac Linux
エディター管理 アクティブなエディターグループを移動 Ctrl+K ← / → ⌘K ← / ⌘K → Ctrl+K ← / Ctrl+K →
デバッグ ステップイン/アウト F11 / Shift+F11 F11 / ⇧F11 F11 / Shift+F11
デバッグ ステップオーバー F10 F10 F10
デバッグ ブレークポイントを切り替え F9 F9 F9
デバッグ 開始/続行 F5 F5 F5
デバッグ 停止 Shift+F5 ⇧F5 Shift+F5
ファイル管理 エクスプローラー/ファインダーでアクティブなファイルを表示 Ctrl+K R ⌘K R Ctrl+K R
ファイル管理 すべて保存 Ctrl+K S ⌥⌘S
ファイル管理 新しいファイル Ctrl+N ⌘N Ctrl+N
ファイル管理 名前を付けて保存... Ctrl+Shift+S ⇧⌘S Ctrl+Shift+S
リッチ言語編集 クイックフィックス Ctrl+. ⌘. Ctrl+.
リッチ言語編集 ファイルの言語を変更 Ctrl+K M ⌘K M Ctrl+K M
一般 ウィンドウ/インスタンスを閉じる Ctrl+Shift+W ⌘W Ctrl+W
一般 新しいウィンドウ/インスタンス Ctrl+Shift+N ⇧⌘N Ctrl+Shift+N
基本的な編集 ワードラップを切り替え Alt+Z ⌥Z Alt+Z
表示 デバッグを表示 Ctrl+Shift+D ⇧⌘D Ctrl+Shift+D
表示 全画面表示を切り替え F11 ⌃⌘F F11

よくわからん

Category Description Windows Mac Linux
エディター管理 フォルダーを閉じる Ctrl+K F ⌘K F Ctrl+K F
ナビゲーション エディターグループの履歴をナビゲート Ctrl+Shift+Tab ⌃⇧Tab Ctrl+Shift+Tab
ナビゲーション 戻る/進む Alt+ ← / → ⌃- / ⌃⇧- Ctrl+Alt+- / Ctrl+Shift+-
ファイル管理 プレビューモードのエディターを開いたままにする Ctrl+K Enter ⌘K Enter Ctrl+K Enter
マルチカーソルと選択 選択範囲を拡大 Shift+Alt+→ ⌃⇧⌘→ Shift+Alt + →
マルチカーソルと選択 選択範囲を縮小 Shift+Alt+← ⌃⇧⌘ ← Shift+Alt + ←
マルチカーソルと選択 列(ボックス)選択 アップ/ダウン ⇧⌥⌘↑ / ↓
マルチカーソルと選択 列(ボックス)選択 ページアップ/ダウン Ctrl+Shift+Alt +PgUp/PgDn ⇧⌥⌘PgUp / PgDn
マルチカーソルと選択 列(ボックス)選択 左/右 ⇧⌥⌘← / →
リッチ言語編集 サイドで定義を開く Ctrl+K F12 ⌘K F12 Ctrl+K F12
リッチ言語編集 パラメーターヒントをトリガー Ctrl+Shift+Space ⇧⌘Space Ctrl+Shift+Space
リッチ言語編集 参照を表示 Shift+F12 ⇧F12 Shift+F12
リッチ言語編集 定義をのぞく Alt+F12 ⌥F12 Ctrl+Shift+F10
リッチ言語編集 提案をトリガー Ctrl+Space, Ctrl+I ⌃Space, ⌘I Ctrl+Space, Ctrl+I
検索と置換 次/前を検索 F3 / Shift+F3 ⌘G / ⇧⌘G F3 / Shift+F3
統合ターミナル 統合ターミナルを表示 Ctrl+` ⌃` Ctrl+`
表示 新しいコマンドプロンプト/ターミナルを開く Ctrl+Shift+C
表示 禅モード(Esc Escで終了) Ctrl+K Z ⌘K Z Ctrl+K Z

おわりに

以上です。
改めて一個一個確認してみましたが、知らないショートカットもいっぱいあって勉強になりました。

【React】 SWR で CRUD 試してみた備忘録

はじめに

業務で SWR を使用する機会があり、プライベートで SWR の勉強をした備忘録です。
CRUD に挑戦しています。
まだ試せたわけではないですが、この記事のように useSWRMutation を使用するより、mutate を使用するほうがパフォーマンス的にはよさそうな気がします。
せっかくなのでだいぶ雑ではありますが、奮闘の記録を残します。

CRUD

今回は GET では useSWR を使用し、それ以外では useSWRMutation を使用する
GET, POST, PUT, DELETE それぞれでカスタムフックを作成する
それぞれ重要なところを数点あげる

前準備

フロントでのエラーハンドリングのために、レスポンスの共通の型を定義

@/types/apiResponse

export type ApiResponse<T> = {
  data: T;
  errorMessage: string;
};

エラーメッセージが突っ込まれるところが毎回違う場合は、公式ドキュメントのようにするのがよさそう
raise するエラーにレスポンスを無理やり付与してる

エラーハンドリング – SWR

GET

import useSWR from "swr";

import type { ApiResponse } from "@/types/apiResponse";

async function fetcher<TData>(url: string): Promise<TData> {
  const response = await fetch(url);
  const result: ApiResponse<TData> = await response.json();

  if (!response.ok) {
    throw new Error(result.errorMessage);
  }

  return result.data;
}

interface UseGetReturns<TData> {
  data: TData | undefined;
  error: Error | undefined;
  isLoading: boolean;
  isValidating: boolean;
}

type Query = Record<string, string>;

export function useGet<TData>(
  url: string,
  query?: Query,
): UseGetReturns<TData> {
  const queryString = new URLSearchParams(query).toString();
  const urlWithQueryString = queryString ? `${url}?${queryString}` : url;

  const { data, error, isLoading, isValidating } = useSWR<TData>(
    urlWithQueryString,
    fetcher,
  );

  return { data, error, isLoading, isValidating };
}
  1. URLクエリをオブジェクトとして受け取って処理
    • URLクエリが変更されると useSWR の key が変更されるので、キャッシュが共有されない
  2. エラーハンドリングのために、fetcher のなかでステータスが 2xx でない時にエラーを投げる
    • useSWRの返り値のerrorにここで投げたErrorが入る
  3. isValidating は POST 後など、データ再検証時に true になる
    • 初期フェッチでまだキャッシュにデータがないときは isLoading を使う
    • データに更新がある時など、データを再取得するときは isValidating を使う
      • キャッシュにあるデータを表示しながら読み込み中であることを表現できる

POST

import useSWRMutation from "swr/mutation";

import type { ApiResponse } from "@/types/apiResponse";

interface FetcherArg<TParams> {
  params: TParams;
}

async function fetcher<TParams, TData>(
  url: string,
  { arg }: { arg: FetcherArg<TParams> },
): Promise<TData> {
  const { params } = arg;

  const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(params),
  });

  const result: ApiResponse<TData> = await response.json();

  if (!response.ok) {
    throw new Error(result.errorMessage);
  }

  return result.data;
}

interface UsePostReturns<TParams, TData> {
  post: (arg: FetcherArg<TParams>) => Promise<TData>;
  isMutating: boolean;
  data: TData | undefined;
  error: Error | undefined;
}

export function usePost<TParams, TData>(
  url: string,
): UsePostReturns<TParams, TData> {
  const { trigger, isMutating, data, error } = useSWRMutation<
    TData,
    Error,
    string,
    FetcherArg<TParams>
  >(url, fetcher);

  return { post: trigger, isMutating, data, error };
}
  1. post関数にPOSTパラメーターをオブジェクトで渡すことでPOSTリクエストを実現
  2. trigger の引数は 1つである必要がありそう
    • なので、複数渡すときはオブジェクトなどで渡す
    • fetcher関数の第二引数のオブジェクトからargキーで取得できる

ex.)

const ENDPOINT = '/api/tasks'
const { tasks } = useGet(ENDPOINT)
const { post } = usePost(ENDPOINT)

const newTask = { title: 'new Task!!' }

post({ params: newTask }) // useGet が再検証され、tasks が更新される
  1. usePost()の url をuseGet()と共通のものにすることで、post()後にuseGet()の再検証が走る
  2. post()で作成したデータをuseGet()で再検証し全データまとめて持ってくる
  3. 差分だけ持ってくるわけではなさそうなので、useGet()が重いときはpost()後のデータ反映が重くなってしまう

PUT

import useSWRMutation from "swr/mutation";

import type { ApiResponse } from "@/types/apiResponse";

interface FetcherArg<TParams> {
  id: number;
  params: TParams;
}

async function fetcher<TParams, TData>(
  url: string,
  { arg }: { arg: FetcherArg<TParams> },
): Promise<TData> {
  const { id, params } = arg;
  const urlWithId = `${url}/${id}`;

  const response = await fetch(urlWithId, {
    method: "PUT",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(params),
  });

  const result: ApiResponse<TData> = await response.json();

  if (!response.ok) {
    throw new Error(result.errorMessage);
  }

  return result.data;
}

interface UsePutReturns<TParams, TData> {
  put: (arg: FetcherArg<TParams>) => Promise<TData>;
  isMutating: boolean;
  data: TData | undefined;
  error: Error | undefined;
}

export function usePut<TParams, TData>(
  url: string,
): UsePutReturns<TParams, TData> {
  const { trigger, isMutating, data, error } = useSWRMutation<
    TData,
    Error,
    string,
    FetcherArg<TParams>
  >(url, fetcher);

  return { put: trigger, isMutating, data, error };
}
  1. post関数同様、put関数にPUTパラメーターをオブジェクトで渡すことでPUTリクエストを実現
  2. put()で作成したデータをuseGet()で再検証し全データまとめて持ってくる
  3. usePut()の url をuseGet()と共通のものにすることで、put()後にuseGet()の再検証が走る
  4. この url をuseGet()と同じものにするために、idはput()経由で渡し、fetcher関数の内部で処理する必要がある

ex.)

const ENDPOINT = '/api/tasks/'

const updateTaskId = 1
const updateTaskParam = { title: 'update Task!!' }

const { tasks } = useGet(ENDPOINT)
const { put_1 } = usePut(`${ENDPOINT}/${updateTaskId}`)
const { put_2 } = usePut(ENDPOINT)

put_1({ params: updateTaskParam }) // useGet が再検証されず、tasks が更新されない
put_2({ id: updateTaskId, params: updateTaskParam }) // useGet が再検証され、tasks が更新される

DELETE

import useSWRMutation from "swr/mutation";

import type { ApiResponse } from "@/types/apiResponse";

interface FetcherArg {
  id: number;
}

async function fetcher<TData>(
  url: string,
  { arg }: { arg: FetcherArg },
): Promise<TData> {
  const { id } = arg;
  const urlWithId = `${url}/${id}`;

  const response = await fetch(urlWithId, {
    method: "DELETE",
    headers: {
      "Content-Type": "application/json",
    },
  });

  const result: ApiResponse<TData> = await response.json();

  if (!response.ok) {
    throw new Error(result.errorMessage);
  }

  return result.data;
}

interface UseDeleteReturns<TData> {
  delete: (arg: FetcherArg) => Promise<TData>;
  isMutating: boolean;
  data: TData | undefined;
  error: Error | undefined;
}

export function useDelete<TData>(url: string): UseDeleteReturns<TData> {
  const { trigger, isMutating, data, error } = useSWRMutation<
    TData,
    Error,
    string,
    FetcherArg
  >(url, fetcher);

  return { delete: trigger, isMutating, data, error };
}
  1. usePut()同様、useDeleteの url をuseGet()と共通のものにすることで、delete()後にuseGet()の再検証が走る
  2. この url をuseGet()と同じものにするために、idはdelete()経由で渡し、fetcher関数の内部で処理する必要がある

ex.)

const ENDPOINT = '/api/tasks/'

const deleteTaskId = 1

const { tasks } = useGet(ENDPOINT)
const { delete_1 } = useDelete(`${ENDPOINT}/${deleteTaskId}`)
const { delete_2 } = useDelete(ENDPOINT)

delete_1({ params: deleteTaskParam }) // useGet が再検証されず、tasks が更新されない
delete_2({ id: deleteTaskId, params: deleteTaskParam }) // useGet が再検証され、tasks が更新される

おわりに

だいぶ武骨にカスタムフックを作成しました。
いつか useSWRMutation がより使いやすくなった時にでも役に立つことを祈ります。
mutateを利用した CRUD をまだ試せてないので、次はそれをやっていきたいなと思ってます。

今回のコードを試したリポジトリもあるので、必要に合わせてご確認ください。
hizahizi-hizumi/react-container-presentational

【VSCode】OS ごとの初期ショートカットキー一覧

はじめに

最近、Windows PC を購入しまして、業務では mac, プライベートでは Windows で開発をしています。 どちらも VSCode を使用しているのですが、ショートカットに微妙に差があったりして戸惑うことがちらほらありました。 そこで、そこら辺の際をまるっと把握してしまおうということで OS ごとの初期ショートカットキーを表にまとめたので共有いたします。

注意

初期ショートカットキーは2024年6月時点の下記を参照しています。
なお、この記事では下記pdf以外の初期ショートカットキーは無視します。

また、pdf からの抽出、markdown テーブルへの加工は LLM を利用しています。

そのうえで人手で調整しています。

予めご了承ください。

表

Category Description Windows Mac Linux
General Show Command Palette Ctrl+Shift+P, F1 ⇧⌘P, F1 Ctrl+Shift+P, F1
General Quick Open, Go to File… Ctrl+P ⌘P Ctrl+P
General New window/instance Ctrl+Shift+N ⇧⌘N Ctrl+Shift+N
General Close window/instance Ctrl+Shift+W ⌘W Ctrl+W
General User Settings Ctrl+, ⌘, Ctrl+,
General Keyboard Shortcuts Ctrl+K Ctrl+S ⌘K ⌘S Ctrl+K Ctrl+S
Basic editing Cut line (empty selection) Ctrl+X ⌘X Ctrl+X
Basic editing Copy line (empty selection) Ctrl+C ⌘C Ctrl+C
Basic editing Move line up/down Alt+ ↑ / ↓ ⌥↓ / ⌥↑ Alt+ ↓ / ↑
Basic editing Copy line up/down Shift+Alt + ↓ / ↑ ⇧⌥↓ / ⇧⌥↑
Basic editing Delete line Ctrl+Shift+K ⇧⌘K Ctrl+Shift+K
Basic editing Insert line below / above Ctrl+Enter / Ctrl+Shift+Enter ⌘Enter / ⇧⌘Enter Ctrl+Enter / Ctrl+Shift+Enter
Basic editing Jump to matching bracket Ctrl+Shift+\ ⇧⌘\ Ctrl+Shift+\
Basic editing Indent/outdent line Ctrl+] / [ ⌘] / ⌘[ Ctrl+] / Ctrl+[
Basic editing Go to beginning/end of line Home / End Home / End Home / End
Basic editing Go to beginning/end of file Ctrl+Home / Ctrl+End ⌘↑ / ⌘↓ Ctrl+ Home / End
Basic editing Scroll line up/down Ctrl+↑ / ↓ ⌃PgUp / ⌃PgDn Ctrl+ ↑ / ↓
Basic editing Scroll page up/down Alt+PgUp / PgDn ⌘PgUp /⌘PgDn Alt+ PgUp / PgDn
Basic editing Fold/unfold region Ctrl+Shift+[ / ] ⌥⌘[ / ⌥⌘] Ctrl+Shift+ [ / ]
Basic editing Fold/unfold all subregions Ctrl+K Ctrl+[ / ] ⌘K ⌘[ / ⌘K ⌘] Ctrl+K Ctrl+ [ / ]
Basic editing Fold/unfold all regions Ctrl+K Ctrl+0 / J ⌘K ⌘0 / ⌘K ⌘J Ctrl+K Ctrl+0 / Ctrl+K Ctrl+J
Basic editing Add line comment Ctrl+K Ctrl+C ⌘K ⌘C Ctrl+K Ctrl+C
Basic editing Remove line comment Ctrl+K Ctrl+U ⌘K ⌘U Ctrl+K Ctrl+U
Basic editing Toggle line comment Ctrl+/ ⌘/ Ctrl+/
Basic editing Toggle block comment Shift+Alt+A ⇧⌥A Ctrl+Shift+A
Basic editing Toggle word wrap Alt+Z ⌥Z Alt+Z
Navigation Show all Symbols Ctrl+T ⌘T Ctrl+T
Navigation Go to Line... Ctrl+G ⌃G Ctrl+G
Navigation Go to File... Ctrl+P ⌘P Ctrl+P
Navigation Go to Symbol... Ctrl+Shift+O ⇧⌘O Ctrl+Shift+O
Navigation Show Problems panel Ctrl+Shift+M ⇧⌘M Ctrl+Shift+M
Navigation Navigate editor group history Ctrl+Shift+Tab ⌃⇧Tab Ctrl+Shift+Tab
Navigation Go back / forward Alt+ ← / → ⌃- / ⌃⇧- Ctrl+Alt+- / Ctrl+Shift+-
Navigation Toggle Tab moves focus Ctrl+M ⌃⇧M Ctrl+M
Navigation Go to next/previous error or warning F8 / Shift+F8 F8 / ⇧F8 F8 / Shift+F8
Search and replace Find Ctrl+F ⌘F Ctrl+F
Search and replace Replace Ctrl+H ⌥⌘F Ctrl+H
Search and replace Find next/previous F3 / Shift+F3 ⌘G / ⇧⌘G F3 / Shift+F3
Search and replace Select all occurrences of Find match Alt+Enter ⌥Enter Alt+Enter
Search and replace Add selection to next Find match Ctrl+D ⌘D Ctrl+D
Search and replace Move last selection to next Find match Ctrl+K Ctrl+D ⌘K ⌘D Ctrl+K Ctrl+D
Search and replace Toggle case-sensitive / regex / whole word Alt+C / R / W
Multi-cursor and selection Insert cursor Alt+Click ⌥ + click Alt+Click
Multi-cursor and selection Insert cursor above / below Ctrl+Alt+ ↑ / ↓ ⌥⌘↑ / ⌥⌘↓ Shift+Alt+ ↑ / ↓
Multi-cursor and selection Undo last cursor operation Ctrl+U ⌘U Ctrl+U
Multi-cursor and selection Insert cursor at end of each line selected Shift+Alt+I ⇧⌥I Shift+Alt+I
Multi-cursor and selection Select current line Ctrl+L ⌘L Ctrl+L
Multi-cursor and selection Select all occurrences of current selection Ctrl+Shift+L ⇧⌘L Ctrl+Shift+L
Multi-cursor and selection Select all occurrences of current word Ctrl+F2 ⌘F2 Ctrl+F2
Multi-cursor and selection Expand / shrink selection Shift+Alt+→ / Shift+Alt+← ⌃⇧⌘→ / ← Shift+Alt+→ / Shift+Alt+←
Multi-cursor and selection Column (box) selection Shift+Alt + (drag mouse) ⇧⌥ + drag mouse Shift+Alt + drag mouse
Multi-cursor and selection Column (box) selection Ctrl+Shift+Alt + (arrow key) ⇧⌥ + drag mouse Shift+Alt + drag mouse
Multi-cursor and selection Column (box) selection page up/down Ctrl+Shift+Alt +PgUp/PgDn ⇧⌥⌘PgUp / ⇧⌥⌘PgDn
Multi-cursor and selection Column (box) selection up/down ⇧⌥⌘↑ / ↓
Multi-cursor and selection Column (box) selection left/right ⇧⌥⌘← / →
Rich languages editing Trigger suggestion Ctrl+Space, Ctrl+I ⌃Space, ⌘I Ctrl+Space, Ctrl+I
Rich languages editing Trigger parameter hints Ctrl+Shift+Space ⇧⌘Space Ctrl+Shift+Space
Rich languages editing Format document Shift+Alt+F ⇧⌥F Ctrl+Shift+I
Rich languages editing Format selection Ctrl+K Ctrl+F ⌘K ⌘F Ctrl+K Ctrl+F
Rich languages editing Go to Definition F12 F12 F12
Rich languages editing Peek Definition Alt+F12 ⌥F12 Ctrl+Shift+F10
Rich languages editing Open Definition to the side Ctrl+K F12 ⌘K F12 Ctrl+K F12
Rich languages editing Quick Fix Ctrl+. ⌘. Ctrl+.
Rich languages editing Show References Shift+F12 ⇧F12 Shift+F12
Rich languages editing Rename Symbol F2 F2 F2
Rich languages editing Trim trailing whitespace Ctrl+K Ctrl+X ⌘K ⌘X Ctrl+K Ctrl+X
Rich languages editing Change file language Ctrl+K M ⌘K M Ctrl+K M
Editor management Close editor Ctrl+F4, Ctrl+W ⌘W Ctrl+W
Editor management Close folder Ctrl+K F ⌘K F Ctrl+K F
Editor management Split editor Ctrl+\ ⌘\ Ctrl+\
Editor management Focus into 1st, 2nd or 3rd editor group Ctrl+ 1 / 2 / 3 ⌘1 / ⌘2 / ⌘3 Ctrl+ 1 / 2 / 3
Editor management Focus into previous/next editor group Ctrl+K Ctrl+ ←/→ ⌘K ⌘← / ⌘K ⌘→ Ctrl+K Ctrl + ← / Ctrl+K Ctrl + →
Editor management Move editor left/right Ctrl+Shift+PgUp / PgDn ⌘K ⇧⌘← / ⌘K ⇧⌘→ Ctrl+Shift+PgUp / Ctrl+Shift+PgDn
Editor management Move active editor group Ctrl+K ← / → ⌘K ← / ⌘K → Ctrl+K ← / Ctrl+K →
File management New File Ctrl+N ⌘N Ctrl+N
File management Open File... Ctrl+O ⌘O Ctrl+O
File management Save Ctrl+S ⌘S Ctrl+S
File management Save As... Ctrl+Shift+S ⇧⌘S Ctrl+Shift+S
File management Save All Ctrl+K S ⌥⌘S
File management Close Ctrl+F4 ⌘W Ctrl+W
File management Close All Ctrl+K Ctrl+W ⌘K ⌘W Ctrl+K Ctrl+W
File management Reopen closed editor Ctrl+Shift+T ⇧⌘T Ctrl+Shift+T
File management Keep preview mode editor open Ctrl+K Enter ⌘K Enter Ctrl+K Enter
File management Copy path of active file Ctrl+K P ⌘K P Ctrl+K P
File management Reveal active file in Explorer Ctrl+K R Ctrl+K R
File management Reveal active file in Finder ⌘K R
File management Show active file in new window/instance Ctrl+K O ⌘K O Ctrl+K O
File management Open next / previous Ctrl+Tab / Ctrl+Shift+Tab ⌃Tab / ⌃⇧Tab Ctrl+Tab / Ctrl+Shift+Tab
Display Toggle full screen F11 ⌃⌘F F11
Display Toggle editor layout (horizontal/vertical) Shift+Alt+0 ⌥⌘0 Shift+Alt+0
Display Zoom in/out Ctrl+ = / - ⌘= / ⇧⌘- Ctrl+ = / -
Display Toggle Sidebar visibility Ctrl+B ⌘B Ctrl+B
Display Show Explorer / Toggle focus Ctrl+Shift+E ⇧⌘E Ctrl+Shift+E
Display Show Search Ctrl+Shift+F ⇧⌘F Ctrl+Shift+F
Display Show Source Control Ctrl+Shift+G ⌃⇧G Ctrl+Shift+G
Display Show Debug Ctrl+Shift+D ⇧⌘D Ctrl+Shift+D
Display Show Extensions Ctrl+Shift+X ⇧⌘X Ctrl+Shift+X
Display Replace in files Ctrl+Shift+H ⇧⌘H Ctrl+Shift+H
Display Toggle Search details Ctrl+Shift+J ⇧⌘J Ctrl+Shift+J
Display Show Output panel Ctrl+Shift+U ⇧⌘U Ctrl+K Ctrl+H
Display Open Markdown preview Ctrl+Shift+V ⇧⌘V Ctrl+Shift+V
Display Open Markdown preview to the side Ctrl+K V ⌘K V Ctrl+K V
Display Zen Mode (Esc Esc to exit) Ctrl+K Z ⌘K Z Ctrl+K Z
Display Open new command prompt/terminal Ctrl+Shift+C
Debug Toggle breakpoint F9 F9 F9
Debug Start/Continue F5 F5 F5
Debug Stop Shift+F5 ⇧F5 Shift+F5
Debug Step into/out F11 / Shift+F11 F11 / ⇧F11 F11 / Shift+F11
Debug Step over F10 F10 F10
Debug Show hover Ctrl+K Ctrl+I ⌘K ⌘I Ctrl+K Ctrl+I
Integrated terminal Show integrated terminal Ctrl+` ⌃` Ctrl+`
Integrated terminal Create new terminal Ctrl+Shift+` ⌃⇧` Ctrl+Shift+`
Integrated terminal Copy selection Ctrl+C ⌘C Ctrl+Shift+C
Integrated terminal Paste into active terminal Ctrl+V Ctrl+Shift+V
Integrated terminal Scroll up/down Ctrl+↑ / ↓ ⌘↑ / ↓ Ctrl+Shift+ ↑ / ↓
Integrated terminal Scroll page up/down Shift+PgUp / PgDn PgUp / PgDn Shift+ PgUp / PgDn
Integrated terminal Scroll to top/bottom Ctrl+Home / End ⌘Home / End Shift+ Home / End

おわりに

思ったより差がなくてびっくりしてます。 mac での行頭、行末への移動が Windows では見当たらなくてこの表を作ろうと思ったのですがあれは VSCode ではなく OS の挙動なのか...

気が向けば日本語版にして再度アップしようと思います。