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呼び出し部分まで自動生成できるようになるので、 かなり便利です。
これを使用して新設計への移行を進めようと思います