RedwoodJS v7.7 Tutorial Chapter 1

Aug 12, 2024

はじめに

フルスタックWebフレームワークであるRedwoodJSのTutorialをやってみた記事です。 TutorialはChapter 0からありますが、実際に手を動かし始めるChapter 1から始めます。

プロジェクト作成

初めはプロジェクト作成です。

yarn create redwood-app --ts ./my-first-redwood-app
success Installed "create-redwood-app@7.7.4" with binaries:
      - create-redwood-app
[##] 2/2------------------------------------------------------------------
                🌲⚡️ Welcome to RedwoodJS! ⚡️🌲
------------------------------------------------------------------
 Compatibility checks passed
 Creating your Redwood app in ./my-first-redwood-app based on command line argument
 Using TypeScript based on command line flag
 Do you want to initialize a git repo? · no / Yes
 Enter a commit message · Initial commit
 Project files created
 Initialized a git repo with commit message "Initial commit"

Thanks for trying out Redwood!

 ⚡️ Get up and running fast with this Quick Start guide: https://redwoodjs.com/quick-start

Fire it up! 🚀

 > cd my-first-redwood-app
 > yarn install
 > yarn rw dev

プロジェクトが作成されたら、パッケージインストール、開発サーバーを起動します。

cd my-first-redwood-app
yarn install
yarn redwood dev

Redwood App

ブラウザが開き、上の画像のような画面になれば OKです。

ファイル構造

├── api
│   ├── db
│   │   └── schema.prisma
│   └── src
│       ├── directives
│       │   ├── requireAuth
│       │   └── skipAuth
│       ├── functions
│       │   └── graphql.ts
│       ├── graphql
│       ├── lib
│       │   ├── auth.ts
│       │   ├── db.ts
│       │   └── logger.ts
│       └── services

├── scripts
│   └── seed.ts

└── web
    ├── public
    │   ├── favicon.png
    │   ├── README.md
    │   └── robots.txt
    └── src
        ├── components
        ├── layouts
        ├── pages
        │   ├── FatalErrorPage
        │   │   └── FatalErrorPage.tsx
        │   └── NotFoundPage
        │       └── NotFoundPage.tsx
        ├── App.tsx
        ├── entry.client.tsx
        ├── index.css
        ├── index.html
        └── Routes.tsx

RedwoodJSではYarnのワークスペース機能を使用しており、apiscriptswebで分かれています。

各ディレクトリの説明は公式ドキュメントを参照ください。

ページの作成

ページの作成は以下コマンドを実行します。

yarn redwood generate page home /
 Generating page files...
 Successfully wrote file `./web/src/pages/HomePage/HomePage.stories.tsx`
 Successfully wrote file `./web/src/pages/HomePage/HomePage.test.tsx`
 Successfully wrote file `./web/src/pages/HomePage/HomePage.tsx`
 Updating routes file...
 Generating types...
 One more thing...
  Page created! A note about <Metadata>:
  At the top of your newly created page is a <Metadata> component,
  which contains the title and description for your page, essential
  to good SEO. Check out this page for best practices:
  https://developers.google.com/search/docs/advanced/appearance/good-titles-snippets

このコマンドで以下ファイルが作成されます。

  • web/src/pages/HomePage/HomePage.tsx: ページコンポーネントファイル
  • web/src/pages/HomePage/HomePage.test.tsx: テストファイル
  • web/src/pages/HomePage/HomePage.stories.tsx: Storybookファイル

さらにルーティング設定としてweb/src/Routes.tsxにパス/が追加されています。

// web/src/Routes.tsx
import { Router, Route } from '@redwoodjs/router'

const Routes = () => {
  return (
    <Router>
      <Route path="/" page={HomePage} name="home" />
      <Route notfound page={NotFoundPage} />
    </Router>
  )
}

export default Routes

リンク

次は別ページを作成して、そこへ遷移してみます。

yarn redwood generate page about

Homeの時は最後にパス/をつけてましたが、今回の場合はパスの指定不要で、指定しない場合はページ名が使われ/aboutになります。

Aboutページが用意できたので、Homeページからリンクさせます。

// web/src/pages/HomePage/HomePage.tsx

import { Link, routes } from '@redwoodjs/router'
import { Metadata } from '@redwoodjs/web'

const HomePage = () => {
  return (
    <>
      <Metadata title="Home" description="Home page" />

      <header>
        <h1>Redwood Blog</h1>
        <nav>
          <ul>
            <li>
              <Link to={routes.about()}>About</Link>
            </li>
          </ul>
        </nav>
      </header>
      <main>Home</main>
    </>
  )
}

export default HomePage

AboutページからHomeページへ戻る場合は以下のようになります。

// web/src/pages/AboutPage/AboutPage.tsx
import { Link, routes } from '@redwoodjs/router'
import { Metadata } from '@redwoodjs/web'

const AboutPage = () => {
  return (
    <>
      <Metadata title="About" description="About page" />

      <header>
        <h1>Redwood Blog</h1>
        <nav>
          <ul>
            <li>
              <Link to={routes.about()}>About</Link>
            </li>
          </ul>
        </nav>
      </header>
      <main>
        <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>
      </main>
    </>
  )
}

export default AboutPage

レイアウト

次はレイアウトを利用して、見た目の共通化を行います。

yarn redwood g layout blog

以下3ファイルが生成されました。

  • web/src/layouts/BlogLayout/BlogLayout.test.tsx
  • web/src/layouts/BlogLayout/BlogLayout.stories.tsx
  • web/src/layouts/BlogLayout/BlogLayout.tsx

BlogLayoutコンポーネントを編集してheaderタグ部分を共通化します。

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

用意したレイアウトをそれぞれHomeとAboutページに適用します。

[About]

// web/src/pages/AboutPage/AboutPage.tsx
import { Link, routes } from '@redwoodjs/router'
import { Metadata } from '@redwoodjs/web'

const AboutPage = () => {
  return (
    <>
      <Metadata 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

[Home]

// web/src/pages/HomePage/HomePage.tsx
import { Metadata } from '@redwoodjs/web'

const HomePage = () => {
  return (
    <>
      <Metadata title="Home" description="Home page" />
      Home
    </>
  )
}

export default HomePage

[Routes]

// web/src/Routes.tsx
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

最後はBlogLayoutコンポーネントにHomeへのリンクを追加します。

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

そして不要になったHomeへのリンクをAboutページから削除します。

import { Metadata } from '@redwoodjs/web'

const AboutPage = () => {
  return (
    <>
      <Metadata 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

ここまででChapter 1は終了です。