(2024年2月現在) Reactの記述方法ってこれがベストプラクティス!みたいなのに結構揺れがある言語(ライブラリ)だと思っていて、いろんなところで意見が分かれる議論が交わされてますよね。まだReact歴も浅くちゃんと最初にベストプラクティスを学びたかったのですが、結局こう!というものがないので、自分で考えて選択するしかない、ということで考察してみました。
【React】 vs 【React + Typescript】
上流から行きましょう。まずはJavascriptのまま書くのか、Typescriptで書くのか。これは最近ではもう「ReactはTypescriptで書く方がよい」というのはだいぶ一般化しているのではないでしょうか。理由は言うまでもなくプログラム自体を堅牢に組み立てられるという、Typescriptのメリットそのままですね。なので以後はReact + Typescriptを前提に話を進めていきます。
【Classコンポーネント】vs【Functionコンポーネント】
これももうすでに決着がついている議論かもしれませんが、FunctionコンポーネントでHooksがサポートがされてからはClassコンポーネントを利用する必要性がなくなり、公式もFunctionコンポーネントを推奨しているので、Functionコンポーネント一択で良いかと思います。
【普通function】 vs 【アローfunction】
// 普通function function Hoge () { ... } // アローfunction const Hoge = () => { ... }
(この呼び方どうなのってとこは置いておいて)ここからですよね。これ、もう早く誰か結論出してよ!っていうくらい意見が分かれてて、色んな記事読みました。結構国内では比較的モダンで記述が少なくて済むアローfunctionの書き方の方がメジャーなのかなという印象ですが、決定打となるようなプロコンが双方になく、もう「決め」の問題でしかないので、以下の理由からこれに関しては私は「普通function」にします!
正確にはReactのファイルの中で、exportするようなモジュール扱いにする関数は普通functionで定義して、その関数内で使用する単発の関数はアローfunctionで記述する、というルールにします。
理由
- どの言語を背景に持つ人でも一発で関数とわかる
- 公式ドキュメントでは軒並み普通functionの表記方法で書かれている
- exportする関数を普通function、その関数の中で使用する単発の関数をアローfunctionで記述することにより役割分担や「this」の対象を明確にできる
【interface】 vs 【type】
// interface interface Person { name: string; age: number; } // type type Person = { name: string; age: number; };
これもよく議論が分かれます。よく見るのがinterfaceの方は継承可能で、知らないところで定義が書き変わってしまうリスクがある、またtypeの方がunionやtupleなど扱える型が多いのでとりあえずtypeにしとけ、という意見でしょうか。
これに関しては以下の記事がわかりやすく参考にさせてもらいました。
特にこの部分ですね。
type :既存の型を用いてオブジェクトの型を定義したい時に使う。 interface :あるモジュール/まとまった処理に対してIFだけ定義したい場合に使う。 実装は継承クラスに任せる。(Javaの使い方と同じイメージ)
結論としてはひとつ前の関数定義のやり方に付随してくるのですが、exportする関数のPropsの型定義はinterfaceを使用し、それ以外はtypeで定義する、というルールにしたいと思います。
Propsの型定義はexportする関数の外側で宣言すると思いますが、function自体も「=」での代入なしの記述で、Propsの型定義をそれと同じ仕様のinterfaceにすることで、そのファイル直下の定義方法に一貫性を持たせられる。また継承に関しては、同じ機能でフォルダでまとめた範囲なら、同じ型を定義することもある、かつ影響も限定できると思うので、namingなどに注意を払いながらうまく利用すれば、有効的に活用できると思っています。
【React.FC】vs【React.VFC】vs【記述ナシ】
// React.FC const MyComponent: React.FC<Props> = ({ children, prop1, prop2 }) => { return ( <div> {children} {/* Component content */} </div> ); }; // React.VFC const MyComponent: React.VFC<Props> = ({ prop1, prop2 }) => { return ( <div> {/* Component content */} </div> ); }; // 記述ナシ const MyComponent = ({ prop1, prop2 }) => { return ( <div> {/* Component content */} </div> ); };
FCでchildrenがデフォルトで渡ってしまうので、childrenを明示的に扱うためVFCが推奨されました => コード修正。FCからchildrenを無くしたのでVFCと同じになりました、なのでVFCはやめてFCを推奨します => コード修正。やってられるかいな!次にどんな気まぐれが降りかかるかわからないので必要に迫られるまでは記述ナシでいきます。
【Props: destructuring(分割代入)しない】or【Props: destructuring(分割代入)する】
// destructing(分割代入)しない function Listing(props: PropsType) { console.log(props.id); console.log(props.name); console.log(props.value); } // destructuring(分割代入)する function Listing({ id, name, value }: PropsType) { console.log(id); console.log(name); console.log(value); }
これはpropsの中身が多かったりすると特にdestructuringしない方が確かにPropsを受け取る部分はスッキリはするんですが、使われていないpropsがあるかなど、何が渡されているのかやはり明示的にわかった方がよいと言うことで、ここは「destructuring(分割代入)する」でいきたいと思います。
以上、最終的に「時と場合による」という結論付けになることが多いReact設定の争点において、自分はこうする!という意思表示をしてみました。そもそも議論が分かれる議題ではあるので、もしご指摘などあれば頂けると勉強の機会になるので嬉しいです。
Happy Coding!