🍇

Apollo Client を基礎から理解する(local state管理編)

2022/02/13に公開

現在のプロジェクトで Apollo Client を触り始めて約半年。いまだに「Apollo Client完全理解した!」 と言えるレベルに至っていないのですが、Apollo Client の最難関「キャッシュ」をはじめとした基本的な使い方についてまとめてみました!

間違っている内容やさらに良い方法等あれば、コメント欄にて(優しく)ご指摘いただけるととても嬉しいです。

はじめに

Apollo Client3から新たに、ローカルデータを管理する便利な機能としてtypePoliciesReactive variablesなどが追加されました。
本記事では、typePoliciesReactive variablesの具体的な使い方についてまとめていきます。

参考
Reactive variables - Apollo GraphQL Docs
TypePolicy fields - Apollo GraphQL Docs

Reactive variables とは

Reactive variablesはキャッシュ(=GraphQLで取得したデータを正規化したもの)から切り離されているため、任意の型や構造のデータを保持することができ、GraphQLスキーマなしでアプリケーションのどこからでも参照・更新することができます。そのため、ReduxやRecoilなどの状態管理ライブラリを使用せずとも、グローバルな状態管理をもApollo Client一つで管理することができます。

具体的な使用方法

Reactive variablesの具体的な使用方法について解説していきます。

1.リアクティブ変数を使うための関数を作成

makeVarメソッドを使用してリアクティブ変数を使うための関数を作成します。

import { makeVar } from '@apollo/client'

// 現在のログイン情報を保持するリアクティブ変数。引数に初期値(false)を指定します。
export const isLoggedIn = makeVar(false)

2. リアクティブ変数を読み取る

リアクティブ変数を読み取るには、以下の2つの方法があります。
注意点として、両者にはリアクティブ変数が更新された時のレンダリングの挙動に違いがあります。

1で作成した関数の引数をなしで呼び出す

リアクティブ変数が更新された場合、リアクティブ変数を読み取っているコンポーネントは再レンダリングはされない

import { isLoggedInVar } from 'apollo/store'

export const Home = () => {
  const isLoggedIn = isLoggedInVar()
  return (
    <>{isLoggedIn ? "ログイン" : "ログアウト"}<>
  )
}

1で作成した関数をuseReactiveVarの引数に指定する

リアクティブ変数が更新された場合、リアクティブ変数を読み取っているコンポーネントは再レンダリングされる

import { useReactiveVar } from '@apollo/client';
import { isLoggedInVar } from 'apollo/store'

export const Home = () => {
  const isLoggedIn = useReactiveVar(isLoggedInVar)
  return (
    <>{isLoggedIn ? "ログイン" : "ログアウト"}<>
  )
}

参考:Storing local state in reactive variables - Apollo GraphQL Docs

3. リアクティブ変数を変更する

1で作成した関数の引数に更新する値を指定します。

import { isLoggedInVar } from 'apollo/store'
isLoggedInVar(true)

typePolicies とは

typePoliciesでは、GraphQLスキーマの各タイプをカスタマイズしたり、キャッシュの識別子を変更したりなどを設定することができます。
具体例を交えながら説明していきます。

使用するGraphQLスキーマ
# ルートオペレーション
type Query {
  "商品一覧"   
  items: [Item!]!
}

# Type
"商品の型"
type Item {
  id: Int!
  name: String!
  price: Int!
} 

キャッシュデータをカスタマイズする

商品の価格の末尾に「円」をつけて表示する方法です。

const cache = new InMemoryCache({
  typePolicies: {
    // ここにカスタマイズしたいTypeを指定します
    Item: {
      fields: {
        price: {
          read(price) {
            return price + '円'
          }
        }
      },
    },
  },
})

Reactive variables と一緒に使う

リアクティブ変数と一緒に使うことで、リアクティブ変数が変更される度に依存する全てのアクティブなクエリが自動的に更新されます。

useQueryを使う方法

import { makeVar } from '@apollo/client'
import { isLoggedInVar } from 'apollo/store'

const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
          // Query(isLoggedIn)を指定します
        isLoggedIn: {
	  read() {
            return isLoggedInVar()
	  }
        }
      },
    },
  },
})
import { currentUserVar } from 'apollo/store'
import { gql,  useQuery }from "@apollo/client";

// クライアントのみで使用するクエリ
const IS_LOGGED_IN = gql`
  query isLoggedIn {
    isLoggedIn @client
  }
`;

export const Home = () => {
  const { isLoggedIn } = useQuery(IS_LOGGED_IN).data;
  return (
    <>{isLoggedIn ? "ログイン" : "ログアウト"}<>
  )
}

useReactiveVarを使う方法

@clientを用いたGraphQLのクエリの記述は不要となり、シンプルな書き方ができます。
その代わり、Apollo Client Devtoolsで確認することができなくなってしまいます。

import { makeVar } from '@apollo/client'
import { isLoggedInVar } from 'apollo/store'

const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
          // Query(isLoggedIn)を指定します
        isLoggedIn: {
	  read() {
            return isLoggedInVar()
	  }
        }
      },
    },
  },
})
import { useReactiveVar } from '@apollo/client';
import { currentUserVar } from 'apollo/store'

export const Home = () => {
  const currentUser = useReactiveVar(currentUserVar)
  return (
    <>{currentUser}<>
  )
}

キャッシュの識別子を変更する

「キャッシュの仕組み編」の記事に記載しております。

まとめ

以上、Reactive variablesとtypePoliciesについてまとめてみました。
この記事が何かお役立ていただけましたら幸いです。

Discussion