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、データベースをやっていきましょう。