背景
ReactにてGraphQLでデータ取得したい、かつ、クラスベースコンポーネントをTypeScriptで実装したいが、Appolo-Client公式のサンプルコードがどうしても動かなかったのでそのメモ。あと、細かいところがどうしてもわからなかったのでどなたかコメントくれたら嬉しいなという期待を込めて。
この記事で書かないこと
GraphQL、React、Typescript、サーバサイドの実装について
サンプルコード
まずはサンプルコードからお見せします。サーバサイドは、term
という検索キーワード的な文字列を受け取ってid, title, description
のリストを返すイメージです。
import React from "react";
import { graphql, ChildProps } from "react-apollo";
import gql from "graphql-tag";
const query = gql`
query GetWorks($term: String) {
works(term: $term) {
id
title
description
}
}
`;
interface Work {
id: string;
title: string;
description: string;
}
interface Response {
works: Work[];
}
type Variables = {
term: string | undefined;
};
interface InputProps extends Variables {}
type Props = ChildProps<InputProps, Response, Variables>;
const ChildWorks = graphql<InputProps, ResWorks, Variables, Props>(query, {
options: ({ term }) => ({
variables: { term }
}),
props: ({ data, ownProps }) => ({ data, ...ownProps })
});
class _Works extends React.Component<Props, {}> {
renderWorks(): JSX.Element[] {
if (this.props.data) {
const { loading, works, error } = this.props.data;
if (loading) return [<div key={0}>Loading...</div>];
if (error) return [<div key={0}>ERROR</div>];
if (works) {
return works.map(work => {
return (
<div key={work.id}>
<h2>{work.title}</h2>
<p>{work.description}</p>
</div>
);
});
} else {
return [<div key={0}>nothing</div>];
}
} else {
return [<div key={0}>loading...</div>];
}
}
render(): JSX.Element {
return <div>{this.renderWorks}</div>;
}
}
export const Works = ChildWorks(_Works);
あとはコンポーネントとして利用できるので、どこかで
<Work term={何かstring} />
としてあげれば実行できます。
説明(可能な部分)
ResponseのTypeとVariableのType、PropsのTypeを指定しています。
InputPropsが空になっていますが、Variableに含めないがPropsとして呼び出し元から値を取りたい場合にはここに追記します。今回はProps=Variablesの前提にしているので空です。
interface Work {
id: string;
title: string;
description: string;
}
interface Response {
works: Work[];
}
type Variables = {
term: string | undefined;
};
interface InputProps extends Variables {}
type Props = ChildProps<InputProps, Response, Variables>;
optionsの部分でPropsで受け取った値をVariablesにセットしています。
propsの部分はなぜこういう記述になるのかは不明、、、どこか海外のサンプルコードから引っ張ってきたはずですがましたが、参照元のURLもすでにわからず。
const ChildWorks = graphql<InputProps, ResWorks, Variables, Props>(query, {
options: ({ term }) => ({
variables: { term }
}),
props: ({ data, ownProps }) => ({ data, ...ownProps })
});
GraphQLで取得したデータはthis.props.data
からアクセスできます。
const { loading, works, error } = this.props.data;
定義したReactコンポーネントそのままでは利用できず、さっき定義したChildWorks
でラップしてあげる必要があるらしい。クエリとクラスコンポーネントを結びつけてあげるイメージかなと。
export const Works = ChildWorks(_Works);
一応、上記で動作してます。
JSXの配列をreturnする際にkeyを設定しないとWarningが出るので無理やりkey={0}
で回避しているのですが、これもどうにかできないものか。。。