はじめに
GraphQLを使う中で、Fragment Colocationという手法があることを知りました。
実際に使ってみて Fragment Colocation を利用することでの利点が見えてきたので、記事にまとめようと思います。
GraphQL の Fragment とは
GraphQL には Fragment という機能があります。
Fragment はクエリのフィールドをまとめ、再利用可能にします。
Fragment を使うと何が嬉しいのか
- 再利用が可能になることで、繰り返し記述することを避けられる
- 繰り返し処理を Fragment としてまとめることで見通しが良くなる
- 仮にフィールド名が変更になったときに複数箇所変更する必要がなくなる
Fragment の実用例
query user {
followers {
id
name
age
}
followees {
id
name
age
}
}
follower フィールドと followee フィールドは同じ User 型のフィールドを参照しています。
id, name, age が重複しており、冗長になっているため、Fragment を使うことで重複を解消します。
query users {
followers {
...userFragment
}
followees {
...userFragment
}
}
fragment userFragment on User {
id
name
age
}
「...フラグメント名」(上の例でいうと ...userFragment
)とすることで Fragment が利用できます。
Fragment Colocation とは
Fragment について理解できたところで、「じゃあ Fragment Colocation とはなんなんだ」という話をしていきます。
Colocate とは、「同じ場所に配置する」という意味です。
Fragment Colocation とは、fragmentを用いてクエリを分割し、データと UI Component を同じ場所に配置する手法です。
Fragment Colocation の実用例
以下のような、ユーザー情報とそのフォロワーの情報を表示するコンポーネントがあるとします。
import { gql, useQuery } from '@apollo/client';
const USER_QUERY = gql`
query userQuery {
user {
name
followers {
id
name
age
}
}
}
`;
export const UserPage: React.FC = () => {
const { data } = useQuery(USER_QUERY);
if(!data?.user) return <div>Not Found<div/>;
return (
<>
<div>
<p>Your Name: {data.user.name}</p>
</div>
<div>
<div>followers</div>
<ul>
{data.user.followers.map((follower) => (
<li key={follower.id}>
<Follower follower={follower} />
</li>
))}
</ul>
</div>
</>
)
}
export type Props = {
readonly follower: {
name
age
}
}
export const Follower: React.FC<Props> = ({ follower }) => {
return (
<>
<div>name: {follower.name}</div>
<div>age: {follower.age}</div>
</>
)
}
フォロワーの名前と年齢に加え、ニックネームも表示したい、という要件が出てきたとします。
そのためには、UserPage.tsx に置いているクエリに nickname フィールドを追加する必要がありそうです。
import { gql, useQuery } from '@apollo/client';
const USER_QUERY = gql`
query userQuery {
user {
name
followers {
id
name
age
nickname
}
}
}
`;
export const UserPage: React.FC = () => {
const { data } = useQuery(USER_QUERY);
if(!data?.user) return <div>Not Found<div/>;
return (
<>
<div>
<p>Your Name: {data.user.name}</p>
</div>
<div>
<div>followers</div>
<ul>
{data.user.followers.map((follower) => (
<li key={follower.id}>
<Follower follower={follower} />
</li>
))}
</ul>
</div>
</>
)
}
export type Props = {
readonly follower: {
name
age
nickname
}
}
export const Follower: React.FC<Props> = ({ follower }) => {
return (
<>
<div>name: {follower.name}</div>
<div>age: {follower.age}</div>
<div>nickname: {follower.nickname}</div>
</>
)
}
本来、 UI の修正としては Follower コンポーネントを修正したいはずなのに、その親の UserPage.tsx にクエリを置いていることで、UserPage.tsx の修正も合わせて必要になっています。
ここで Fragment Colocation の出番です。
import { gql, useQuery } from '@apollo/client';
import { FOLLOWER_FIELDS } from './Follower';
const USER_QUERY = gql`
query userQuery {
user {
name
followers {
id
...followerFields
}
}
}
${FOLLOWER_FIELDS}
`;
export const UserPage: React.FC = () => {
const { data } = useQuery(USER_QUERY);
if(!data?.user) return <div>Not Found<div/>;
return (
<>
<div>
<p>Your Name: {data.user.name}</p>
</div>
<div>
<div>followers</div>
<ul>
{data.user.followers.map((follower) => (
<li key={follower.id}>
<Follower follower={follower} />
</li>
))}
</ul>
</div>
</>
)
}
import { gql } from '@apollo/client';
export const FOLLOWER_FIELDS = gql`
fragment followerFields on User {
name
age
nickname
}
`
export type Props = {
readonly follower: {
name
age
nickname
}
}
export const Follower: React.FC<Props> = ({ follower }) => {
return (
<>
<div>name: {follower.name}</div>
<div>age: {follower.age}</div>
<div>nickname: {follower.nickname}</div>
</>
)
}
Fragment として name, age, nickname フィールドを Follower.tsx に置くことで、変更が本来修正したいファイルのみで完結します。
Fragment Colocation を使わない場合、他にどんな困りごとがありそうか
追加の場合はクエリを変更しないと情報が取れないため、「追加のし忘れ」のようなことは起こらないと思います。
しかし、逆にいらなくなったフィールドが出てきたときを考えてみると、UI コンポーネントとデータフェッチの場所が離れていた(Fragment Colocation を使わずに全て親のコンポーネントにクエリを置いていた)場合、気づくことができるでしょうか?
「クエリからフィールドを削除しなければいけないかもしれない」と気付いたとして、親コンポーネントがたくさんの子コンポーネントを持っていた場合、他のコンポーネントも確認した上で本当に削除して良いフィールドなのかを判断する必要が出てきそうです。
上記の例だとかなり単純な構造なので、あるフィールドが不要になった場合の修正も用意ではありますが、実務だともっと複雑な構造であることが多いと思います。
そんな時に Fragment Colocation を使い、データと UI コンポーネントを同じ位置に置くことで、「どこでどの情報を使っているのか?」がわかりやすくなり、管理もしやすくなります。
さいごに
Fragment Colocation を理解したことで、コンポーネントの保守性を高めることができるようになりました。
実務でもどんどん Fragment Colocation を活用していきたいと思います。
参考
https://graphql.org/learn/queries/#fragments
https://zenn.dev/so_nishimura/articles/2c8796761f2d02