👩‍💼

const Hoge: React.FC<Props>=()=>って書いてたら思考停止系と言われたので調べた

に公開
5

Discussion

クロパンダクロパンダ

読んでて気になったのですが、React.FC と FC には差があるので、React.FC を使っていたことを指摘してたのではないでしょうか?

というのも JSX を使うために React をインポートしなくて良くなった という背景があります。なので FC ではなく React.FC を使う(=React をインポートする)と tree shaking が効かなくなります。そのことを指摘してた可能性はないでしょうか?

あと、react のドキュメントは基本的にコードを JS で書いています。つまり(おそらく) TS 的な都合を考慮したドキュメントになってません。なので、TS + React の時にも function を使うべきかは一考の余地があると思います。

いろいろ書きましたが僕も function については深く考えずアロー関数の優位性を持ってそっちが良いと思ってたので、他の派閥が気になってます。

TFTF

Next.js公式でもfunctionですね。他人のコードを読むときに、明示的にfunctionと記述してあると瞬殺でこれは関数であることがわかるから、とどこかで読んだ気がします。
また、Classコンポーネントで実装していた名残で、functionのthisがグローバルのwindowをポイントしていたこともあるかるかもしれません。(つまり、プロジェクトごとに事情は変わる。)

UhucreamUhucream

「思考停止系エンジニア?」の意図というか真意が分かりませんが、個人的に気になったのは、

const Hoge: React.FC<Props> = () => {}

export default Hoge // ここ

が気になりました。

export const Hoge: React.FC<Props> = () => {}

とも書けますので。

後者の場合、import の際に名前が宣言時の名前と一致する(?)メリットがあります

import { Hoge } from '../hoge'
Hidden comment
Hidden comment
smikitkysmikitky

何故かこれ系のほかの記事でも言及されていないことが多いのですが、FC を使う最大の実用上の理由は「FC返り値の型チェックがあるから」です。コンポーネントとは props を受け取るだけではダメで、ReactNode を return して初めてコンポーネントです。FC はそのチェックを行います。

// 😢これらはコンポーネントではない、オブジェクトを返す謎の関数に過ぎない
// …が、エラーにならず定義できてしまう
const Hoge = (props: Props) => { return new Date(); };
function Hoge(props: Props) { return new Date(); }

// 👍これは書いた瞬間に型エラーになる
const Hoge: FC<Props> = () => { return new Date(); }

上側の方法でも実際に JSX 内で使おうとした時点で分かりづらいエラー(要するに「謎の関数を JSX 内で使うな」的なエラー)が出てそこでミスに気づけはします。が、ローカルでコンポーネントを書きながらその場で型エラーが出てくれるのは実用上のメリットです。短いコンポーネントならともかく、条件分岐が含まれた長いコンポーネントではうっかり分岐のどこかで変なことをして、実際に FC の返り値チェックにお世話になることがしばしばあります。

コンポーネント定義時に (props: Props): ReactNode と毎回明示的に返り値の型を書くことで FC と概ね同様の型安全性にはできますし、React コンポーネント程度なら入出力の型を独立して明示的に書いても大したことではありません。が、FC などの関数型は引数と返り値の型を1フレーズで同時にチェックするために存在するので、使えるなら使うべきだと思います。あと厳密には FCHoge.displayName が文字列であることなど他の型チェックも行ってくれます。

FC の最大の問題だった暗黙の children は型ファイルの設計ミスの類であって React 18 の時点で修正済なので、2 年以上前の古い記載に基づいて「よう分からんけど FC はダメ」とならないで欲しいなと思っています。