React Query と GraphQL Code Generator で Mutation

React Query を単体で使わずいきなり GraphQL Code Generator と組み合わせたために Mutation の実行方法がわからず手こずったので結論を書く。

開発環境

  • react: 18.0.0
  • graphql: 15.8.0
  • @graphql-codegen/typescript: 2.4.11
  • @graphql-codegen/typescript-operations: 2.4.0
  • @graphql-codegen/typescript-react-query: 3.5.12
  • react-query: 3.39.1
  • typescript: 4.6.3
  • vite: 2.9.7

本文

前提として既に『React Query と GraphQL Code Generator 初期設定』の内容は終わっているものとする。

またスキーマは以下とする。

schema.graphqls

クエリの定義

今回の例として ./src/api/query.graphql に下記のクエリを記述する。

mutation updateBlog($input: UpdateBlogInput!) {
  updateBlog(input: $input) {
    id
    blogKey
    author
    name
    description
    publishOption
    createdAt
    updatedAt
    links {
      id
      name
      url
    }
  }
}

プロジェクトルート(codegen.yaml と同じ階層)で

npm run generate

のコマンドを実行すると、./src/api/generated.ts にコードが生成される。
生成内容を抜粋する。

export type UpdateBlogMutationVariables = Exact<{
  input: UpdateBlogInput;
}>;


export type UpdateBlogMutation = { __typename?: 'Mutation', updateBlog?: { __typename?: 'Blog', id: string, blogKey: string, author: string, name: string, description: string, publishOption: PublishOption, createdAt: string, updatedAt: string, links?: Array<{ __typename?: 'BlogLink', id: string, name: string, url: string } | null> | null } | null };

export const UpdateBlogDocument = `
    mutation updateBlog($input: UpdateBlogInput!) {
  updateBlog(input: $input) {
    id
    blogKey
    author
    name
    description
    publishOption
    createdAt
    updatedAt
    links {
      id
      name
      url
    }
  }
}
    `;
export const useUpdateBlogMutation = <
      TError = any,
      TContext = unknown
    >(options?: UseMutationOptions<UpdateBlogMutation, TError, UpdateBlogMutationVariables, TContext>) =>
    useMutation<UpdateBlogMutation, TError, UpdateBlogMutationVariables, TContext>(
      ['updateBlog'],
      (variables?: UpdateBlogMutationVariables) => fetcher<UpdateBlogMutation, UpdateBlogMutationVariables>(UpdateBlogDocument, variables)(),
      options
    );

生成された hook の使用

最小のコードを抜粋すると、以下のようにすると動く。

import {
  UpdateBlogInput,
  UpdateBlogMutationVariables,
  useUpdateBlogMutation,
  // 中略
} from "../../api/generated";

// 中略

export function Index() {
  // 中略
  const { mutateAsync } = useUpdateBlogMutation({
    onError: (e: any) => {
      console.error(e);
    },
    onSuccess: () => {
      console.log('success');
    },
  });
  const mutate = async (variables: UpdateBlogMutationVariables) =>
    await mutateAsync({
      input: variables.input,
    });

  const onSubmit = (data: any) => {
    const input: UpdateBlogInput = {
      id: blogId,
      author: data.author
    };
    mutate({ input });
  };

  return (
    <div>
      // 中略
    </div>
  );
}

ポイントとしては useUpdateBlogMutation はあくまでも hook なのでトップレベルでしか呼べない。

トップレベルに書いた時点ではまだ Mutation は動かず mutateAsync というのが取れる。
mutateAsync を呼ぶことによって Mutation を実行できる。

mutateAsync 以外にも色々使えるので公式ドキュメント参照。

useMutation

以上