Firestoreの新設計に作り直しの作業が終わらないので、一旦途中記事を作成
https://github.com/wheatandcat/Peperomia/pull/709/files
新設計に合わせてアプリ側もGraphQLで再実装中です。
graphql-codegenとは
前に別記事で触れましたが、graphql-codegenを導入するとgraphqlのスキーマから自動でtypeファイルを生成してくれます
webでの実装は、ここまでで終了していましたが、ReactではHooks部分まで自動生成できるみたいなので実装してみました。
graphql-codegen/typescript-react-apolloとは
graphql-codegen/typescript-react-apolloを導入することで、Hooks、Component、HOCなどを自動生成することが可能になります。 ペペロミアでは、ほぼ全てReact Hooksに移行が完了しているので、Hooksの部分を自動生成して使用していきます。
実装
まず、graphql-codegen/typescript-react-apolloのインストール
$ yarn add @graphql-codegen/typescript-react-apollo
gqlファイルを作成します。
■ src/queries/calendar.gql
query Calendar($date: String!) {
calendar(date: $date) {
id
date
item {
id
title
kind
itemDetails {
id
title
kind
memo
url
place
priority
}
}
}
}
graphql-codegenの設定ファイルを追加
■ codegen.yml
overwrite: true
schema:
- ./schema.graphqls
documents:
- "./src/queries/**/*.gql"
generates:
./src/queries/api/index.ts:
hooks:
afterOneFileWrite:
- yarn codegen:lint:fix
plugins:
- typescript
- typescript-operations
- typescript-react-apollo
config:
skipTypename: false
withHooks: true
withHOC: false
withComponent: false
apolloReactHooksImportFrom: '@apollo/client'
withHooksをtrueに設定しています
withHooks: true
最後にcodegenを実行するscriptを追加
■ package.json
"scripts": {
...(略)
"download:schema.graphqls": "curl -L -O https://raw.githubusercontent.com/wheatandcat/PeperomiaBackend/master/graph/schema.graphqls",
"codegen": "npm run download:schema.graphqls && graphql-codegen",
"codegen:lint:fix": "eslint --fix ./src/queries/api/index.ts"
これで自動生成までの実装は完了、以下のコマンドを実行する
$ yarn codegen
以下のファイルが自動生成されます。
■ src/queries/api/index.ts
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
import * as ApolloReactHooks from '@apollo/client';
...略)
export type Calendar = {
__typename?: 'Calendar';
id: Scalars['ID'];
/** 日付 */
date: Scalars['String'];
/** true: パブリック、false: プライベート */
public: Scalars['Boolean'];
/** スケジュール */
item: Item;
};
...略)
export type CalendarQueryVariables = Exact<{
date: Scalars['String'];
}>;
export type CalendarQuery = { __typename?: 'Query' } & {
calendar?: Maybe<
{ __typename?: 'Calendar' } & Pick<Calendar, 'id' | 'date'> & {
item: { __typename?: 'Item' } & Pick<Item, 'id' | 'title' | 'kind'> & {
itemDetails?: Maybe<
Array<
Maybe<
{ __typename?: 'ItemDetail' } & Pick<
ItemDetail,
| 'id'
| 'title'
| 'kind'
| 'memo'
| 'url'
| 'place'
| 'priority'
>
>
>
>;
};
}
>;
};
...略)
/**
* __useCalendarQuery__
*
* To run a query within a React component, call `useCalendarQuery` and pass it any options that fit your needs.
* When your component renders, `useCalendarQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useCalendarQuery({
* variables: {
* date: // value for 'date'
* },
* });
*/
export function useCalendarQuery(
baseOptions?: ApolloReactHooks.QueryHookOptions<
CalendarQuery,
CalendarQueryVariables
>
) {
return ApolloReactHooks.useQuery<CalendarQuery, CalendarQueryVariables>(
CalendarDocument,
baseOptions
);
}
export function useCalendarLazyQuery(
baseOptions?: ApolloReactHooks.LazyQueryHookOptions<
CalendarQuery,
CalendarQueryVariables
>
) {
return ApolloReactHooks.useLazyQuery<CalendarQuery, CalendarQueryVariables>(
CalendarDocument,
baseOptions
);
}
export type CalendarQueryHookResult = ReturnType<typeof useCalendarQuery>;
export type CalendarLazyQueryHookResult = ReturnType<
typeof useCalendarLazyQuery
>;
export type CalendarQueryResult = Apollo.QueryResult<
CalendarQuery,
CalendarQueryVariables
>;
export const CalendarsDocument = gql`
query Calendars($startDate: String!, $endDate: String!) {
calendars(startDate: $startDate, endDate: $endDate) {
id
date
item {
id
kind
}
}
}
`;
...略)
と、CalendarのtypesとHooksファイルが生成されます。
●自動生成されたHooks
export function useCalendarQuery(
baseOptions?: ApolloReactHooks.QueryHookOptions<
CalendarQuery,
CalendarQueryVariables
>
) {
return ApolloReactHooks.useQuery<CalendarQuery, CalendarQueryVariables>(
CalendarDocument,
baseOptions
);
}
上記のHooksをComponentで使用すると、以下みたいな感じになります。
■ src/components/pages/Calendar/Connected.tsx
import React, { memo, useCallback } from 'react';
import { useCalendarQuery } from 'queries/api/index';
import { Props as IndexProps } from './';
import Plain, { QueryProps } from './Plain';
export type CalendarType = QueryData<QueryProps, 'calendar'>;
export type ItemDetailType = ArrayType<CalendarType['item']['itemDetails']>;
type Props = IndexProps & {
date: string;
};
export type ConnectedType = {
onDismiss: () => void;
};
const Connected: React.FC<Props> = memo((props) => {
const { data, loading, error } = useCalendarQuery({
variables: {
date: props.date,
},
});
return (
<Plain data={data} loading={loading} error={error} />
);
});
export default Connected;
実装的には、useCalendarQueryをimportして
import { useCalendarQuery } from 'queries/api/index';
variablesを設定してあげれば、そのまま値の取得が可能です。
const Connected: React.FC<Props> = memo((props) => {
const { data, loading, error } = useCalendarQuery({
variables: {
date: props.date,
},
});
データの再取得したい場合は、variablesを変えるかrefetchを呼べばOKです。
と、こんな感じでgraphql-codegen/typescript-react-apolloを使えばAPI呼び出し部分まで自動生成できるようになるので、 かなり便利です。
これを使用して新設計への移行を進めようと思います