挨拶
こんにちは.
後の祭りで盆踊りを踊れる男 とこ,身長188cm => T.Miuraです.
ここ最近半年前のrepositoryを見たのですが,あまりにもひどいコードを書いていてご飯が喉に通らなかったです.
たった半年で気持ち悪さに気がつけるほど成長したということでいいのでしょうか?
この記事はReact/Typescript 初心者が孤軍奮闘した結果のoutputです.
優しい目で見てください
Props とは?
https://ja.react.dev/learn/passing-props-to-a-component
React コンポーネントが外から受け取る値の束が props
TypeScript では props の形を 型に当てはめてcomponentsで受け取っています.
ひとえにpropsと言っても,受け取り方に種類が多いです.
私自身もまだまだ理解できていない部分が多く,時折脳が揮発して調べに行きます.
様々な Props の受け取り方
よく見る主要なprops の受け取り方を書いていきます.
(props: Props)
use case : Props 全体をひとまとまりで扱いたいときの基本形.
type TitleProps = {
title: string;
padding: string;
};
const Title = (props: TitleProps) => {
return (
<>
<h1>{props.title}</h1>;
<p>{props.padding}</p>
</>
)
};
<Title title="props をそのまま使う" padding="オーソドックス!"/>
オーソドックスで公式ドキュメントでもよく見る書き方です(三浦統計)
Propsの内容を分割せずに一つで受け取りプロパティアクセスで運用していくものです
分割せずに受け取れるので受け取るときに見通しがいいですね.
ただ,type が入れ子になっていると長くなってしまうのがデメリットになります
以下はかなり簡略化した例です
type Props = {
title: string;
rest: PropsProps;
};
type PropsProps ={
padding: string;
}
const Title = (props: Props) => {
return (
<>
<h1>{props.title}</h1>;
<p>{props.rest.padding}</p>
</>
)
};
Lintの no-unused-vars も propsが1回でも使われていればエラーを出さないのでプロパティ使い忘れたことがありました.
({ title }: Props)
use case : Props の一部だけを使いたいときは分割代入を併用する.
type Props = {
title: string;
padding: string;
};
const Title = ({ title, padding }: Props) => {
return (
<>
<h1>{title}</h1>;
<p>{padding}</p>
</>
)
};
<Title title="分割代入!" padding="これが一番好き!"/>
私が一番好きなpropsの受け取り方ですね.
分割している分,各propsごとにlint が働くので使い忘れがないです
プロパティアクセスがない分,見やすいのが利点ですね.
デメリットとして下記のようにpropsが多いと見通しが悪くなります
type Props = {
title: string;
padding: string;
h1: string;
h2: string;
h3: string;
};
//長い...
const Title = ({
title,
padding,
h2,
h3
}: Props) => {
return (
<>
<h1>{title}</h1>;
<h2>{h2}</h2>
<h3>{h3}</h3>
<p>{padding}</p>
</>
)
};
<Title title="分割代入!" padding="これが一番好き!" h2="!!!" h3="!!!!"/>
({ title, ...rest }: Props)
use case : 特定の値だけ名前で取り出し、残りをそのまま別コンポーネントへ受け渡したいとき
type BlogProps = {
title: string;
padding: string;
tags:string[];
};
const Title = ({ title, ...rest}: BlogProps) => {
return (
<>
<h1>{title}</h1>;
<Rest {...rest} />
</>
)
};
<Title title="一部分割!" padding="知らなかった..." tags={['react','typescript']}/>
私自身はこの書き方をしたことがありませんでした.子コンポーネントに渡す際に活躍するようです
(なんならこの発想すらなかった)
具体的な説明ですが, type BlogProps からtitle のみを除いたものがrestになります
つまりrestは下記になります
{
padding:string,
tag:Tag[]
}
これは使っていきたい!!
({ size = "md" }: Props)
use case : Props に初期値を持たせたいときは分割代入とデフォルト値を組み合わせる.
type ButtonProps = {
size?: "sm" | "md" | "lg";
};
const Button = ({ size = "md" }: ButtonProps) => {
return <button data-size={size}>size の初期値</button>;
};
<Button />
これもよく見ますね!
shadcnや他UIコンポーネントの実装を見ると大体あります.
({ blogs }: { blogs: Blog[] })
他のコンポーネント用の Props 型をそのまま再利用して渡したい場合.(インライン型)
type Blog = {
id: string
title: string;
};
const BlogList = ({ blogs }: { blogs: Blog[] }) => {
return (
<>
blogs.map((b)=>{
return(
<div key={b.id}>
<p>{b.title}</p>
</div>
)
})
</>
);
};
これはたまに使います.
コンポーネント専用の Props 型を外出しするほどでもない時に使っていました!
デメリットとして,例えばBlog[]を多数componentsで作るようになった時は管理が面倒ということです.
また,再利用性が崩れ,変更が入ったときに大変面倒です.
型設計やチームの方針にもよるとは思いますが
まとめ
| Props の記法 | 使いどき(ユースケース) | やめた方がいい場面(アンチパターン) |
|---|---|---|
(props: Props) |
Props をひとまとまりとして扱いたいとき / props の構造が浅く、読みやすいとき / 分割せずに props.xxx で扱う方が自然なとき |
Props が深くネストしているとき(props.a.b.c の連続) / 利用しないプロパティが増え可読性が落ちるとき |
({ title, padding }: Props) |
メインの値を明示的に取り出したいとき / props の視認性を高めたいとき | プロパティ数が多い場合 / 分割代入が冗長な場合 |
({ title, ...rest }: Props) |
特定のキーだけ取り出し、残りを下層コンポーネントに渡すとき / 「親コンポーネントが拡張されても子に自然に伝搬したい」ケース | rest が肥大化し、どの値が rest に入っているか判別しづらいとき / 子に渡す props を明示的に管理したいプロジェクト方針のとき |
({ size = "md" }: Props) |
・UI コンポーネントでデフォルト値を持たせたいとき / shadcn/ui のように variant + size のパターンを提供したいとき | デフォルト値が複雑(オブジェクト・関数)なとき / 親側で必ず明示的に指定すべき値のとき |
({ blogs }: { blogs: Blog[] })(インライン型) |
「わざわざ外部に型を作るほどでもない」一時的コンポーネント / マイクロコンポーネントで型の乱立を避けたいとき |
Blog などのモデル型を複数コンポーネントで使うとき / 型の再利用性が求められる状況 / 変更時に多数の箇所を修正しないといけなくなる場合 |