High Performance React Web 開発: Layout.tsxの解説
概要
下記リポジトリを参考に、typeScriptの勉強を進めているので、不明点の洗い出しと理解した内容を書き出す。解説するコードについては、末尾に記載している。
ReactNode
1行目でインポートしているReactNodeについて、様々な型を子コンポーネントで使用できる。interface Propsで、Propsが様々な型を定義できるように設定している。ただ、anyとは異なり、どんな型でも入れられる訳ではない。ReactNodeは、DOMが返す型を決めることができる。そしてここで決めた型が、Layoutに渡されている。
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
ただし、指定する型が多く、記述者の意図を伝えるために、型を絞った方が良い場合はReact
ChildやReactTextを使うと良い。
type ReactChild = ReactElement | ReactText;
type ReactText = string | number;
ReactNodeだと、stringが受け入れられない定義になっているが、ReactNodeの中にはReactTextが入っており、その中ではstringを受け入れることができる。つまり、ReactNode、ReactChild、ReactTextでstringを扱うことができる。今回の例でいえば、childrenをReactNodeではなく、ReactTextに書き換える形とすれば、stringを扱うことができる。
Props
Propsは親子関係で、親から子に同様の定義を付与する形で使われる。例えば下記の例で言うと、Layoutという関数はPropsで定義した型のみ使える関数として、子コンポーネントの定義がされている。他にも、export const Sidebar: FC Propsといった形で親コンポーネント定義→子コンポーネントのような使い方ができる。つまり、親の定義によって子コンポーネントの使用用途が一括で決められる。
export const Layout: FC<Props>
FC
1行目でインポートしているFCについて、まず下記を例に文法を理解する。
interface GEN<T>{
item: T
# この時点では、GENの型がTと定義されており、定まっていない。
}
# ここでGENの型をstringと定義し、型が決まる。
const gen0: GEN<string> = {item: "hello" };
# 型をつけない場合はエラーとなる。
const gen1: GEN = {item: "hello" };
const gen2: GEN<number> = {item: 12 };
Layout
FC項目で説明の通り、TとPropsで定義の型のみ、関数内で受け取ることが可能である。Propsについては、children: ReactNodeと記述しているため、概ねの型を受け取ることが可能である。
export const Layout: FC<Props> = ({ children }) => {
return <div>
</div>
}
ここで使われるLayout: FCは、React独自のタグとして、別の場所で使える。この場合だと、下記に記載のとおり。実際にApp.tsxでLayoutタグが使用されている。また、returnの中身がコンポーネントの材料として作られる。例えば、Sidebarの場合は、Sidebarに必要な要素をreturnの中で定義すれば良い。
<Layout></Layout>
interface Props
下記のように定義した場合、NAMEではfirstのstringとlastのstringとnullのみ受け付ける。let nameObjでは定義した通り引数を入れているので、エラーなく処理される。もしfirst: 1といった形で、数値を入れている場合はエラーが発生する。
interface NAME {
first: string;
last: string | null;
}
let nameObj: NAME = { first: "Yamada", last: null }
Link
Linkは簡易的に記載すると、下記仕様で別URLへ遷移できる。
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
参考文献
コード
import { ReactNode, FC } from 'react'
import { Link } from 'react-router-dom'
interface Props {
children: ReactNode
}
export const Layout: FC<Props> = ({ children }) => {
return (
<div className="flex justify-center items-center flex-col min-h-screen text-gray-600 font-mono">
<header>
<nav className="bg-gray-800 w-screen">
<div className="flex items-center pl-8 h-14">
<div className="flex space-x-4">
<Link
className="text-sm text-gray-300 hover:bg-gray-700 px-3 py-2 rounded"
to="/"
>
react-query
</Link>
<Link
className="text-sm text-gray-300 hover:bg-gray-700 px-3 py-2 rounded"
to="/fetch-a"
>
Regular fetch
</Link>
<Link
className="text-sm text-gray-300 hover:bg-gray-700 px-3 py-2 rounded"
to="/main-context"
>
useContext
</Link>
<Link
className="text-sm text-gray-300 hover:bg-gray-700 px-3 py-2 rounded"
to="/main-rtkit"
>
RTkit
</Link>
</div>
</div>
</nav>
</header>
<main className="flex flex-1 flex-col justify-center items-center w-screen">
{children}
</main>
</div>
)
}
Discussion