負債化しにくいフロントエンド開発 (全体編)
3月までサービスサイトのリプレイスを行っていました。今回は、その開発時に使っていたきれいなコードを書くための指針を紹介しようと思います。 話が長くなるので、コンポーネントや状態管理やGraphqlの話を除いた全体的な箇所について、紹介しようかと思います。 フロントエンドの構成は、ReactとNextJsを使っていました。汎用的な内容なので、React以外のフレームワークでも参考になると思います。
1.配置
- 関連するものは近くにおく
- ファイルやロジック型、依存するもの関連するものの近くに置く
- Bad: src/types/RadarChart.d.ts
- Good: src/..../RadarChart/RadarChart.d.ts
理由
近くに置くことで依存関係を明確にすることができます。 変更が多いフロントエンドでは、使われなくなり参照されていないコードが残っていくということが起きやすいのですが、機能を取り除くタイミングで関連するコードをきちんと消すことができます。 副作用として嬉しいのが、ファイルが探しやすくなることです。開発時にファイルの探すコストは結構大きいですが、ファイルの探すコストが下がり、開発体験が上がります。
2.基本的な命名
- 変数関数は、camelCaseを使う
- Reactの仕様で仕方なく、コンポーネントのみPascalCaseを使う
- SID に従う 名前は短く、直感的で説明的でなければなりません
- 短い。名前は入力に時間がかからないようにする必要があります。したがって、覚えておいてください。
- 直感的。名前は、できるだけ一般的なスピーチに近い自然な読み方をする必要があります。
- 説明的。名前は、最も効率的な方法で機能/所有するものを反映する必要があります。
- 収縮を使用しない
- Bad:
onItmClk
- Good:
onItemClick
- Bad:
- メンタルマップを避ける
- Bad:
nutritions.map(n => [n.name](http://n.name/))
- Good:
nutritions.map(nutrition => [nutrition.name](http://nutrition.name/))
- Bad:
- コンテキストの重複を避ける
- Bad:
class MenuItem { handleMenuItemClick = (event) => { ... }
- Good
class MenuItem { handleClick = (event) => { ... }
- Bad:
- 名前は、バックエンドの表現と一致させる
- Bad:
Search
- Good:
ItemSearch
- Bad:
3.関数
関数の名前
- A/HC/LC Patternに従う
引数
- 引数は最大2つまで、それ以上は、オブジェクト化する
分岐
- 関数内で分岐するよりかはデフォルト引数を使う
// Bad
function loadPages(count?: number) {
const loadCount = count !== undefined ? count : 10;
// ...
}
// Good
function loadPages(count: number = 10) {
// ...
}
副作用を持たない関数を定義する
- 外側に影響を与えず、引数に影響も影響を与えない関数を作ることで、予期しない変更を防ぎコードの可読性を上げる
- コンポーネント内で呼び出される関数についていうと、コンポーネントの外に関数を定義したほうが、より明示的な純粋関数を作ることができる。
カスタムhooks
- カスタムhooksは、1階層まで、useXXXという名前にする
- すなわち、カスタムhooksの中で、カスタムhooksを呼び出してはいけない。
- 抽象化の失敗したカスタムhooksは、かえってコードの理解の妨げになる。
- DRYよりも、理解しやすさを優先する
4.型定義
名前
- CamelCaseで良い
- ただ、実態と同じ名前の型を使いたいときはTをつける
配置場所
- 依存関係を意識した配置場所にすること
- コンポーネントで使う場合は、コンポーネントのすぐそばに定義する。
- コンポーネントから、exportして使いたい場合は、
- コンポーネントが定義されたindex.tsxでexportするか。
- componentName.tsファイルを作り、そこで exportすること
5.Export
- exportするときは、default exportではなく、export const でexportすること
- ただし、ページは、nextjsの機能で、ページとして扱われるためにdefault exportが必要なため、それだけ例外とする
- プロジェクトで決めておくことなので、決めておく。
6.コンポーネントの型定義
- コンポーネントの型とpropsは以下のように定義すること
- ただし、見ずらさや、使い回ししたい場合は、type Propsとして型定義しても良い
const ComponetName: React.FC<{
name: string
}> = ({ name }) => {
}
7.非同期
- async/awaitを使う
指針の利用の感想
- 実際に開発で使った感想として、複数人で開発してもコードの品質が落ちず、コードの可読性を上げることができたので、よい指針だったなと思いました。
- 負債化しないかどうかは、運用していってからわかることなので、もしかしたら、今後、こうしておけばよかったなどは出るかもしれませんが、様子を見守って行けたらなと思います。
- 事前にルールが決まっていると考えることがより本質的な箇所に集中でき、開発の効率化につながると感じたので、指針を事前にプロジェクトで決めておくのはお得感がある行動だと感じました。