Skip to main content
Version: 5.3

Cells

前のページの最後に挙げた機能(ロード状態、エラーメッセージ、空白のテキスト)は、ほとんどのWebアプリで一般的なものです。私たちは、典型的なコンポーネントにこれらの機能を追加する際に、開発者の生活を楽にするために何ができるか考えました。私たちは、そのための方法を編み出しました。これを Cells (セル)と呼んでいます。セルはデータ取得のための、よりシンプルで宣言的なアプローチを提供します(セルに関する完全なドキュメントを読む)。

これらのロード状態に加えて、セルは自分自身のデータ取得にも責任を持ちます。つまり、親コンポーネントでデータを取得して、それを必要とする子コンポーネントに props を渡すのではなく、セルは完全に自己完結しており、自分自身のデータを取得して表示します!ブログにセルを追加して、セルがどのように動作するか見てみましょう。

セルを作成する際には、いくつかの特別な名前の定数をエクスポートし、Redwoodがそれを処理します。典型的なセルは次のようなものです:

export const QUERY = gql`
query FindPosts {
posts {
id
title
body
createdAt
}
}
`

export const Loading = () => <div>Loading...</div>

export const Empty = () => <div>No posts yet!</div>

export const Failure = ({ error }) => (
<div>Error loading posts: {error.message}</div>
)

export const Success = ({ posts }) => {
return posts.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
<div>{post.body}</div>
</article>
))
}

React がこのコンポーネントをレンダリングすると、Redwood は QUERY を実行し、レスポンスを受け取るまでは Loading コンポーネントを表示します。

クエリ結果が返ってきたら、3つの状態のうち1つが表示されます:

  • エラーが発生した場合は Failure コンポーネントが表示される
  • 返されたデータが空だった場合( null または空の配列)は Empty コンポーネントが表示される
  • それ以外の場合は Success コンポーネントが表示される

また beforeQueryQUERY に渡す前の props を操作する)や afterQuery (GraphQL から返されるが Success コンポーネントに送られる前のデータを操作する)といったライフサイクルヘルパーも用意されています。

セルで最低限必要なのは QUERYSuccess のエクスポートです。 Empty コンポーネントをエクスポートしない場合、空の結果が Success コンポーネントに送信されます。 Failure コンポーネントがない場合、コンソールにエラー出力が送られます。

セルをいつ使うかのガイドラインは、コンポーネントがデータベースや他のサービスから何らかのデータを必要とし、その応答が遅れる可能性がある場合です。いつ何を表示するかはRedwoodに任せて、受け取ったデータでレンダリングされたコンポーネントというハッピーパスに集中することができます。

Our First Cell

通常、ブログではホームページに最近のブログ記事のリストが表示されます。このリストは、私たちの最初のセルにピッタリです。

Wait, don't we already have a home page?

データベースからデータが必要な場合、一般的に セル を使いたいと思うでしょう。Redwoodのベストプラクティスは、アプリが持つ固有のURLごとにページを作成し、セルでデータを取得して表示することです。そのため、既存の HomePage はこの新しいセルを子ページとしてレンダリングします。

これから繰り返し見ていきますが、Redwoodにはこの機能のためのジェネレータがあります! "Posts" はすでに scaffold ジェネレータで使われていたので、これを "Articles" セルと呼ぶことにしましょう(雛形ファイルは Post ディレクトリに作成されました)。名前がお互いにかなり異なる方が、頭の中で整理しやすいでしょう。複数のものを表示するつもりなので、 "Article" ではなく、複数形の "Articles" を使うことにします。

yarn rw g cell Articles

このコマンドは /web/src/components/ArticlesCell/ArticlesCell.tsx に新しいファイルを作成します( test.tsxmock.tsstories.tsx ファイルも作成されます)。詳しくはチュートリアルの第5章で詳しく説明します!)。このファイルには、使い始めに必要な boilerplate がいくつか含まれています:

web/src/components/ArticlesCell/ArticlesCell.js
export const QUERY = gql`
query ArticlesQuery {
articles {
id
}
}
`

export const Loading = () => <div>Loading...</div>

export const Empty = () => <div>Empty</div>

export const Failure = ({ error }) => (
<div style={{ color: 'red' }}>Error: {error.message}</div>
)

export const Success = ({ articles }) => {
return (
<ul>
{articles.map((item) => {
return <li key={item.id}>{JSON.stringify(item)}</li>
})}
</ul>
)
}
Indicating Multiplicity to the Cell Generator

セルを生成するときには、大文字小文字いずれも使うことができ、Redwoodは命名するときにこれを正します。これらはすべて同じファイル名( web/src/components/BlogArticlesCell/BlogArticlesCell.tsx )で作成されます:

yarn rw g cell blog_articles
yarn rw g cell blog-articles
yarn rw g cell blogArticles
yarn rw g cell BlogArticles

複数の単語を使用していることを示す 何らかの しるしが必要です:スネークケース( blog_articles )、ケバブケース( blog-articles )、キャメルケース( blogArticles ), パスカルケース(BlogArticles)のいずれかです。

(2つの単語を使っていることを示さずに) yarn redwood g cell blogarticles を実行すると、 web/src/components/BlogarticlesCell/BlogarticlesCell.tsx にファイルが生成されます。

できるだけ早く実行できるように、ジェネレータはセルと同じ名前のルートGraphQLクエリを持っていると仮定し、データベースから何かを取得するために必要な最小限のクエリを提供します。この場合、クエリの名前は articles です:

web/src/components/ArticlesCell/ArticlesCell.js
export const QUERY = gql`
query ArticlesQuery {
articles {
id
}
}
`

しかし、これは既存の Posts SDL ( api/src/graphql/posts.sdl.ts ) やサービス ( api/src/services/posts/posts.ts )では有効なクエリ名ではありません(これらのファイルがどこから来たのかは Getting DynamicCreating Post Editor セクション をご覧ください)。 Redwoodは便宜上、クエリ要素にセル自体の名前を付けていますが(多くの場合、特定のモデルのためにセルを作成します)、今回はセル名がモデル名と一致していないので、手動で微調整をする必要があります。

クエリ名と Success の props の両方を posts に変更する必要があります:

web/src/components/ArticlesCell/ArticlesCell.js
export const QUERY = gql`
query ArticlesQuery {
posts {
id
}
}
`

export const Loading = () => <div>Loading...</div>

export const Empty = () => <div>Empty</div>

export const Failure = ({ error }) => (
<div style={{ color: 'red' }}>Error: {error.message}</div>
)

export const Success = ({ posts }) => {
return (
<ul>
{posts.map((item) => {
return <li key={item.id}>{JSON.stringify(item)}</li>
})}
</ul>
)
}

このセルを HomePage に差し込んで、どうなるか見てみましょう:

web/src/pages/HomePage/HomePage.js
import { MetaTags } from '@redwoodjs/web'

import ArticlesCell from 'src/components/ArticlesCell'

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

export default HomePage

ブラウザには、データベースにあるブログ記事の id と GraphQL 固有の __typename プロパティが実際に表示されるはずです。もし "Empty" と表示されるだけなら 前回作成した scaffold に戻り、いくつかブログ記事を追加してください。いいですね!

Showing articles in the database
info

Success コンポーネントの posts はどこからきたのでしょうか?

この QUERY ステートメントで呼び出しているクエリは posts です。このクエリの名前が何であれ、それが Success でデータを利用できるようになる props の名前になります。

export const QUERY = gql`
query ArticlesQuery {
posts {
id
}
}
`

また、GraphQLクエリの結果を格納した変数名をエイリアスにすると、それが props の名前になります:

export const QUERY = gql`
query ArticlesQuery {
articles: posts {
id
}
}
`

これで posts の代わりに articlesSuccess で使えるようになります:

export const Success = ({ articles }) => { ... }

それでは前述のエイリアスを使用して、セルの名前と反復処理するデータの一貫性を保つようにしましょう:

web/src/components/ArticlesCell/ArticlesCell.js
export const QUERY = gql`
query ArticlesQuery {
articles: posts {
id
}
}
`

export const Loading = () => <div>Loading...</div>

export const Empty = () => <div>Empty</div>

export const Failure = ({ error }) => (
<div style={{ color: 'red' }}>Error: {error.message}</div>
)

export const Success = ({ articles }) => {
return (
<ul>
{articles.map((item) => {
return <li key={item.id}>{JSON.stringify(item)}</li>
})}
</ul>
)
}

ジェネレータによって query に追加された id に加えて、 titlebodycreatedAt の値も取得しましょう:

web/src/components/ArticlesCell/ArticlesCell.js
export const QUERY = gql`
query ArticlesQuery {
articles: posts {
id
title
body
createdAt
}
}
`

このページには、scaffoldで作成したブログ記事のデータのすべてが表示されます:

Articles with all DB values

さて、ここからは古き良きReactコンポーネントの領域です。ブログ記事をいい感じに表示するために、Success コンポーネントを作り上げます:

web/src/components/ArticlesCell/ArticlesCell.js
export const Success = ({ articles }) => {
return (
<>
{articles.map((article) => (
<article key={article.id}>
<header>
<h2>{article.title}</h2>
</header>
<p>{article.body}</p>
<div>Posted at: {article.createdAt}</div>
</article>
))}
</>
)
}

ついに私たちはブログサイトを開設しました!インターネットを飾る最も基本的なブログかもしれませんが、それがどうした!ブログ記事の作成、編集、削除ができ、ホームページで世界中の人が見ることができます(心配しないでください、もっと多くの機能を追加する予定です)。

Nicely formatted blog articles

Summary

おさらいですが、ここまで実際に何をしてきたのでしょうか?

  1. ホームページを作成
  2. ブログのレイアウトを作成
  3. データベーススキーマを定義
  4. マイグレーションを実行してデータベースを更新しテーブルを作成
  5. データベーステーブルへのCRUDインターフェイスの足場作り(scaffold)
  6. データをロードするセルを作成し、loading/empty/failure/successの状態を管理
  7. セルをページに追加

最後の数ステップは、Redwoodアプリで新機能を構築する際の標準的なライフサイクルです。

今のところ、ちょっとしたHTML以外は、手作業はほとんどありません。特に、データをある場所から別の場所に移動するためだけのコード(plumbing)をたくさん書く必要はありませんでした。こうすると、Web開発はもうちょっと楽しくなると思いませんか?

これからこのアプリにいくつかの機能を追加していきますが、まずは少し寄り道して、Redwoodがデータベースにアクセスする方法と、SDLやサービスファイルが何のためにあるのかを知りましょう。