自分は React で開発する際 default export を推奨するよ、という話
Qiita 開発チームが React の開発で default export を使わなくなった理由
この記事を読んでみての感想です。
よく default export
を使うべきだの named export
を使うべきだの議論を目にしますが。
自分としてはぶっちゃけどっちでも良いです、どーでも良いです、興味がないです。
ただ、チームとして開発する場合 export
に関するルールはしっかり決めておくべきだよなーとは思います。
なので、Qiita の開発チームが named export
に寄せたルールのもと開発を行っていくというのは良いことなんじゃないかなーと思います。
named export
を使用するルールを敷いたことについては良いことだと思いますが、一方で以下の 2 点については少し気になりました。
- Qiita 開発チームが default export を使わなくなった理由は、default export だと import する側で自由に名前を決められてしまうためです。
- Atomic Design では、部品ごとにパーツを作り、さらに組み立てたコンポーネントをつくっていくため、どうしてもコンポーネントの数が多くなってしまいます。そこで default export を使うと、コードを書く人によってコンポーネント名が変わる可能性があり、コンポーネントの管理が難しくなるといった問題が考えられます。
default export は import する側で自由に名前を決められてしまう件
あくまで自分の開発手法における考え方ですが。
自身がテックリードを務める現場の場合、1 ファイル 1 コンポーネントというルールを敷いて開発を行うことがほとんどです。
つまり、コンポーネントを扱うファイル内では 1 コンポーネントしか扱うことを許容せず、そのコンポーネントは必ず default export
することを強制します。
import React, { FC } from "react";
export type HogeProps = {
hoge: string;
};
const Hoge: FC<HogeProps> = ({ hoge }) => <div>{hoge}</div>;
export default Hoge;
で、もう少し言うと、コンポーネント以外のファイルも可能な限り 1 ファイル 1 default export
で統一するようにしています。
特に custom hooks や Redux 周りについては 1 ファイル 1 default export
を強制するようにしています。
なぜこのようなルールを敷くかというと、1 ファイル内に複数のコンポーネントや複数のモジュールを切ってほしくないからです。
1 ファイル 1 default export
のルールで開発を行うと、必然的に 1 ファイル内の記述量が減り、ファイルごとの見通しが良くなります。
もちろんその分ファイル数は増えるのですが、個人的にはファイルごとの目的を明確にしたいため、ファイル数が増えることは問題だと思っていません。
それよりは、保守的な観点から見たときに、ファイルごとの目的が薄まることのほうが危ないかなーと思っています。
例えば Hoge
というコンポーネントファイルを作成した際、そのファイル内に Fuga
というコンポーネントが作成されていると『ん?』と思うんですよね。
import React, { FC } from "react";
// ん?と思う
const Fuga: FC = () => <div>fuga</div>;
export type HogeProps = {
hoge: string;
};
export const Hoge: FC<HogeProps> = ({ hoge }) => (
<div>
<Fuga />
<div>{hoge}</div>
</div>
);
100 歩譲ってその Fuga
が named export
されていなければ、Hoge
のみで使用されるコンポーネントなのかなーと推察されますが。(自身がテックリードの場合は許容しませんが)
Fuga
が named export
されている場合、『なんで Hoge
コンポーネントファイル内で Fuga
コンポーネントが export 可能な状態なの…?』と感じてしまいます。
import React, { FC } from "react";
// このファイル名が Hoga.tsx の場合すごく気持ち悪い
export const Fuga: FC = () => <div>fuga</div>;
export type HogeProps = {
hoge: string;
};
export const Hoge: FC<HogeProps> = ({ hoge }) => (
<div>
<Fuga />
<div>{hoge}</div>
</div>
);
で、これって保守観点からすると、保守性の低い状態だよなーと強く感じます。
また、Hoge
と Fuga
が並列なレベルに存在するのも気持ち悪いですし、そもそも Hoge
と HogeProps
が並列なのも気持ち悪いです、あくまで個人的にですが。
なのでコンポーネントファイルにおいては、そのファイル名と同一のコンポーネントを default export
させる、というルールを採用するようにしています。
で、ようやくここから本題なのですが、default export
を使用した場合、import する側で自由に名前を変えれてしまう、という件ですが。
まぁ、イヤっちゃイヤですよね、勝手に名前変えられるのって。
自分がテックリードを務める現場でも、import してきたコンポーネントの名前がファイル名とズレている場合指摘します。
でもこれってあくまで import する側の言い分であって、default export
するか named export
するかは import される側の問題なのではないかなーと、あくまで個人的にですが。
ちなみに、MDN には以下のように書かれています。
Named exports are useful to export several values. During the import, it is mandatory to use the same name of the corresponding object.
名前付きエクスポートは、いくつかの値をエクスポートするのに役立ちます。 インポート中は、対応するオブジェクトと同じ名前を使用する必要があります。
なので、import する際に名前が変えられるから named export
を使用するというのは、そもそもの目的からはちょっと外れているんじゃないかな?と感じました。
コンポーネントの数が多くなり、コンポーネントの管理が難しくなった
これも気持ちはよーくわかります。
いくら Atomic Design を導入したとしても、いくら Storybook を導入したとしても、コンポーネントの数が増えれば管理するのが厳しくなってきます。
が、そもそも論ではあるのですが、コンポーネントの管理を行うのってフロントエンドエンジニアではなく UI / UX デザイナーの仕事だと個人的には思っています。
UI / UX デザイナーがきちんとコンポーネントの管理・整理を行い、コンポーネントごとに的確な名前を定め、Atomic Design のもとしっかりとしたプロトタイピングを行えば、そもそもフロントエンドエンジニアはコンポーネントの管理で困ることはありませんし、ファイル名云々の問題もそこまで発生しません。
記事中では以下のように書かれていますが。(抽象化ってなんだ…?)
SubmitButton を抽象化し、Button にした場合
この文面だけ見ると、Qiita の開発チームではフロントエンドエンジニアが全てのコンポーネントの管理を行い、フロントエンドエンジニアが Atomic Design を導入し、フロントエンドエンジニアがファイル名を定めているように見受けられました、あくまで予想ですが。
で、Qiita くらいの規模であれば、フロントエンドエンジニアがコンポーネントの管理を行うのって、厳しそうだよなーと思います。
なので、余計なお世話であることは承知の上で、もし UI / UX デザイナーがコンポーネントの管理を行っていないのであれば、早急に補強を行ったほうが良いのでは?と、勝手に思いました。
JavaScript における export
周りについては様々な書き方ができるため、逆に難易度が上がっているよなーと感じます。
まぁ JavaScript 自体がそういう言語なのでしょーがないとは思うのですが。
なので、フロントエンドエンジニアは開発を行う際、JavaScript の自由度の高さに甘えるのではなく自らルールを定めて縛る必要が出てきます。
そういった意味で Qiita の開発チームが named export
で統一している、というのは本当に立派なことだなーと思います。
一方、自分がテックリードを務める場合は default export
を強制したり、1 ファイル 1 コンポーネントを強制します、基本的に例外は認めません。
過去の経験上、UI / UX デザイナーとフロントエンドエンジニアがしっかりと連携しているプロジェクトなんて日本にはほぼ存在しませんし、そうなると必然的にフロントエンドエンジニアがコンポーネントを管理するしかありません。
eslint にも import/no-default-export
や import/prefer-default-export
のようなルールも存在しますので、しっかりと縛ってやることが保守性の向上に繋がるのかなーと。