Layouts
<header> の重複を解決する一つの方法は、 <Header> コンポーネントを作成して、それを HomePage と AboutPage の両方で読み込むことです。それは有効ですが、もっと良い方法はないでしょうか?理想を言えば <header> への唯一の参照はコードのどこからでも行えるべきです。
この2つのページを見たとき、彼らが本当に気にしていることは何でしょうか?表示したいコンテンツがあるのです。その前( <header> など)や後( <footer> など)を気にする必要は全くないはずです。そこでレイアウトの出番です:レイアウトはページをコンポーネントでラップし、その子としてページをレンダリングします。レイアウトは、ページ自体の外側にある任意のコンテンツを含むことができます。概念的には、最終的にレンダリングされるドキュメントは次のような構造になっています:

<header> を保持するためのレイアウトを作りましょう:
yarn redwood g layout blog
ここからは generate の代わりに、より短いエイリアスである g を使います。
web/src/layouts/BlogLayout/BlogLayout.tsx と、関連するテストファイルやストーリーファイルが作成されました。これを "blog" レイアウトと呼んでいるのは、将来的に他のレイアウト("admin" レイアウトとか?)を用意するかもしれないからです。
HomePage と AboutPage の両方から <header> を切り取って、代わりにレイアウトに貼り付けます。重複している <main> タグも取り除いてしまいましょう:
- JavaScript
- TypeScript
import { Link, routes } from '@redwoodjs/router'
const BlogLayout = ({ children }) => {
return (
<>
<header>
<h1>Redwood Blog</h1>
<nav>
<ul>
<li>
<Link to={routes.about()}>About</Link>
</li>
</ul>
</nav>
</header>
<main>{children}</main>
</>
)
}
export default BlogLayout
import { Link, routes } from '@redwoodjs/router'
type BlogLayoutProps = {
children?: React.ReactNode
}
const BlogLayout = ({ children }: BlogLayoutProps) => {
return (
<>
<header>
<h1>Redwood Blog</h1>
<nav>
<ul>
<li>
<Link to={routes.about()}>About</Link>
</li>
</ul>
</nav>
</header>
<main>{children}</main>
</>
)
}
export default BlogLayout
- JavaScript
- TypeScript
import { Link, routes } from '@redwoodjs/router'
import { MetaTags } from '@redwoodjs/web'
const AboutPage = () => {
return (
<>
<MetaTags title="About" description="About page" />
<p>
This site was created to demonstrate my mastery of Redwood: Look on my
works, ye mighty, and despair!
</p>
<Link to={routes.home()}>Return home</Link>
</>
)
}
export default AboutPage
import { Link, routes } from '@redwoodjs/router'
import { MetaTags } from '@redwoodjs/web'
const AboutPage = () => {
return (
<>
<MetaTags title="About" description="About page" />
<p>
This site was created to demonstrate my mastery of Redwood: Look on my
works, ye mighty, and despair!
</p>
<Link to={routes.home()}>Return home</Link>
</>
)
}
export default AboutPage
- JavaScript
- TypeScript
import { MetaTags } from '@redwoodjs/web'
const HomePage = () => {
return (
<>
<MetaTags title="Home" description="Home page" />
Home
</>
)
}
export default HomePage
import { MetaTags } from '@redwoodjs/web'
const HomePage = () => {
return (
<>
<MetaTags title="Home" description="Home page" />
Home
</>
)
}
export default HomePage
BlogLayout.tsx では、children がマジックが起こる場所です。レイアウトに与えられたすべてのページコンテンツは、ここでレンダリングされます。これで、ページは関心のあるコンテンツに集中できるようになります(レイアウトが肩代わりしてくれるので HomePage から Link と routes のインポートを削除することができます)。
レイアウトを実際にレンダリングするには、Routesファイルを変更しなければなりません。 <Set> を使って HomePage と AboutPage を BlogLayout でラップします。ページとは異なり、レイアウトでは実際に import ステートメントが必要です:
- JavaScript
- TypeScript
import { Router, Route, Set } from '@redwoodjs/router'
import BlogLayout from 'src/layouts/BlogLayout'
const Routes = () => {
return (
<Router>
<Set wrap={BlogLayout}>
<Route path="/about" page={AboutPage} name="about" />
<Route path="/" page={HomePage} name="home" />
</Set>
<Route notfound page={NotFoundPage} />
</Router>
)
}
export default Routes
import { Router, Route, Set } from '@redwoodjs/router'
import BlogLayout from 'src/layouts/BlogLayout'
const Routes = () => {
return (
<Router>
<Set wrap={BlogLayout}>
<Route path="/about" page={AboutPage} name="about" />
<Route path="/" page={HomePage} name="home" />
</Set>
<Route notfound page={NotFoundPage} />
</Router>
)
}
export default Routes
src aliasimport文では src/layouts/BlogLayout を使用しており、 ../src/layouts/BlogLayout や ./src/layouts/BlogLayout ではないことに注意してください。
src だけで使えるのは、Redwood が提供する便利機能です: src は現在のワークスペースにある src パスのエイリアスです。つまり web で作業している場合は、 src は web/src を指し、 api では api/src を指します。
ブラウザに戻ると(手動でリロードする必要があるかもしれません)、...何も変わっていないはずです。しかし、これは良いことです。レイアウトが機能しているということです!
Redwoodのファイル名の中に重複があることにお気づきかもしれません。ページは /pages というディレクトリにあり、その名前には Page が含まれています。レイアウトも同じです。どうしたことでしょう?
エディタで何十ものファイルを開いていると、すぐ迷子になります。似たような、あるいは(たまたま違うディレクトリにある)同じ名前のファイルがいくつかある場合は特に。index.ts という名前のファイルが何十個もあって、開いているタブの中から探すのを想像してみてください!ファイル名の中の重複は、開いている特定のファイルを探すときの生産性向上に寄与することがわかりました。
React Developer Tools プラグインを使用している場合、コンポーネントスタックをブラウズする際に曖昧さをなくすのにも役立ちます:

Back Home Again
さらに <Links> を:タイトルとロゴはホームに戻るようリンクし、ホームへのナビリンクも追加します:
- JavaScript
- TypeScript
import { Link, routes } from '@redwoodjs/router'
const BlogLayout = ({ children }) => {
return (
<>
<header>
<h1>
<Link to={routes.home()}>Redwood Blog</Link>
</h1>
<nav>
<ul>
<li>
<Link to={routes.home()}>Home</Link>
</li>
<li>
<Link to={routes.about()}>About</Link>
</li>
</ul>
</nav>
</header>
<main>{children}</main>
</>
)
}
export default BlogLayout
import { Link, routes } from '@redwoodjs/router'
type BlogLayoutProps = {
children?: React.ReactNode
}
const BlogLayout = ({ children }: BlogLayoutProps) => {
return (
<>
<header>
<h1>
<Link to={routes.home()}>Redwood Blog</Link>
</h1>
<nav>
<ul>
<li>
<Link to={routes.home()}>Home</Link>
</li>
<li>
<Link to={routes.about()}>About</Link>
</li>
</ul>
</nav>
</header>
<main>{children}</main>
</>
)
}
export default BlogLayout
そうしたら、Aboutページにあった余計な "Return to Home" リンク(と、Linkとroutesのimport)を削除できます:
- JavaScript
- TypeScript
import { MetaTags } from '@redwoodjs/web'
const AboutPage = () => {
return (
<>
<MetaTags title="About" description="About page" />
<p>
This site was created to demonstrate my mastery of Redwood: Look on my
works, ye mighty, and despair!
</p>
</>
)
}
export default AboutPage
import { MetaTags } from '@redwoodjs/web'
const AboutPage = () => {
return (
<>
<MetaTags title="About" description="About page" />
<p>
This site was created to demonstrate my mastery of Redwood: Look on my
works, ye mighty, and despair!
</p>
</>
)
}
export default AboutPage

進捗しました!重複していた部分をすべて削除し、ヘッダコンテンツ(ロゴとナビゲーション)を一カ所にまとめました。
ここまでの作業はすべてWebサイド、つまりブラウザ上でのことです。それでは、バックエンドに着手して、GraphQL、Prisma、データベースをやっていきましょう。