ApolloのFragmentの使い方

## はじめに

小さなLTに挑戦してみたので、その時に調べた事をまとめました
LTの内容を [Apolloのキャッシュとリストの更新](https://qiita.com/toki_k/items/945670f580c5cf507d97)の記事と2つに分割しています。

## 使用技術

* React
* Apollo Client
* GraphQL Code Generator
* Hasura(GraphQLサーバー側。今回はそこまで関係ないかも)

## サンプルアプリ
### 画面イメージ
![スクリーンショット 2021-09-24 13.16.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506867/2046a11a-ea33-4a44-b875-5ada179974be.png)

### DB
usersテーブル：id,　name
todosテーブル：id,　user_id,　title,　created_at,　updated_at

userがtodoのリストを持っている

### ソースコード

https://github.com/tokio-k/LT-sample-apollo-fragment-and-modify-cache
([Apolloのキャッシュとリストの更新](https://qiita.com/toki_k/items/945670f580c5cf507d97)の記事の内容も入っています)

## 書き方

TodoFragmentを作る場合

```.ts
gql`
  fragment Todo on todos {
    id
    title
    created_at
    updated_at
  }
`
```

## 用途

### 再利用できるため、書く量が減る&変更に強くなる

**Fragmentなし**

```.ts
gql`
  query GetUser($id: Int!) {
    users_by_pk(id: $id) {
      id
      name
      todos {
        id
        title
        created_at
        updated_at
      }
    }
  }
`;
gql`
  mutation AddTodo($user_id: Int!, $title: String!) {
    insert_todos_one(object: { title: $title, user_id: $user_id }) {
      id
      title
      created_at
      updated_at
    }
  }

  mutation UpdateTodo($id: Int!, $title: String!) {
    update_todos_by_pk(pk_columns: { id: $id }, _set: { title: $title }) {
      id
      title
      created_at
      updated_at
    }
  }
`;
```

**Fragmentあり**

```.ts
gql`
  query GetUser($id: Int!) {
    users_by_pk(id: $id) {
      id
      name
      todos {
        ...Todo
      }
    }
  }
`;
gql`
  mutation AddTodo($user_id: Int!, $title: String!) {
    insert_todos_one(object: { title: $title, user_id: $user_id }) {
      ...Todo
    }
  }

  mutation UpdateTodo($id: Int!, $title: String!) {
    update_todos_by_pk(pk_columns: { id: $id }, _set: { title: $title }) {
      ...Todo
    }
  }
`;
gql`
  fragment Todo on todos {
    id
    title
    created_at
    updated_at
  }
`;
```

一つの変更で済みますね

### 簡単に型の定義ができる

****

**propsの型に使う**

```.ts
{data?.users_by_pk?.todos.map((todo) => { // users_by_pk・・① todo・・②
  return (
    <li key={todo.id}>
      <Todo todo={todo} />
    </li>
  );
})}
```

```graphql.ts

// ①のusers_by_pkの型(自動生成された)
Maybe<(
    { __typename?: 'users' }
    & Pick<Users, 'id' | 'name'>
    & { todos: Array<(
      { __typename?: 'todos' }
      & Pick<Todos, 'id' | 'title' | 'created_at' | 'updated_at'>
    )> }
  )>

// ②のtodoの型(自動生成された)
{ __typename?: 'todos' }
& Pick<Todos, 'id' | 'title' | 'created_at' | 'updated_at'>

// TodoFragmentで生成された型(自動生成された)
export type TodoFragment = (
  { __typename?: 'todos' }
  & Pick<Todos, 'id' | 'title' | 'created_at' | 'updated_at'>
);
```
②のtodoの型とTodoFragmentで生成された型は同じ

```.ts
type TodoType = { todo: TodoFragment };

const Todo: VFC<TodoType> = ({ todo }) => {
...
...
}
```
### キャッシュの書き換えで使用

user1がTodoを追加するMutationの後
user1のTodoリストを更新する場合のイメージ

```.ts
const [addTodo] = useAddTodoMutation({
  update(cache, { data }) {
    cache.modify({
      id: cache.identify({ id: 1, __typename: "users" }),
      fields: {
        todos(existing = []) {
          const newTodoRef = cache.writeFragment({
            data: data?.insert_todos_one,
            fragment: TodoFragmentDoc, //←これ
          });
          return [...existing, newTodoRef];
        },
      },
    });
  },
});
```

([Apolloのキャッシュとリストの更新](https://qiita.com/toki_k/items/945670f580c5cf507d97)の記事に詳細を記載)

## まとめ

Fragmentの使用方法

1. 再利用できるため、書く量が減る&変更に強くなる
1. 簡単に型の定義ができる
1. キャッシュの書き換えで使用

## 参考記事

https://www.apollographql.com/docs/react/data/fragments/