はじめに
今日も今日とて、フロントReact + バックRailsの...
ということで、今回は今更ながら graphql-code-generator の便利さを痛感するの続きとして、登録、削除等のMutation
の部分をやっていきます。
※構成は前回と同じです。
とりあえずApolloの公式に沿ってやってみる
まずはTODOを追加する仕組みを作っていきます。
追加用のテキストフィールドを用意しました。
ここに値を入力して、エンターで追加処理が実行される仕組みとします。
+import { gql, useMutation } from "@apollo/client";
...
+const ADD_TODO = gql`
+ mutation addTodo($name: String!) {
+ addTodo(input: { name: $name }) {
+ todo {
+ id
+ name
+ }
+ }
+ }
+`;
const App = () => {
const { loading, data } = useTodosQuery();
+ const [addTodo] = useMutation(ADD_TODO);
...
</p>
+ <input
+ type="text"
+ onKeyPress={(e) => {
+ if (e.key === "Enter") {
+ addTodo({ variables: { name: e.currentTarget.value } });
+ }
+ }}
+ />
{loading ? (
...
文字を入力して、エンターで追加処理が実行できる仕組みができました。
エンター押下後、画面をリロードすると、TODOが追加されていることがわかります。
リロードしないと動きがわからないので、ついでに、追加処理に加え、入力欄のクリア、一覧情報の再取得処理をやっておきます。
...
- const { loading, data } = useTodosQuery();
+ const { loading, data, refetch } = useTodosQuery();
- const [addTodo] = useMutation(ADD_TODO);
+ const [addTodo] = useMutation(ADD_TODO, {
+ update() {
+ refetch();
+ },
+ });
...
onKeyPress={(e) => {
if (e.key === "Enter") {
addTodo({ variables: { name: e.currentTarget.value } });
+ e.currentTarget.value = "";
}
}}
特に型関連でエラーは起きませんでした。
もう少し便利になるよう作り込んでみる
何も入力せずに、登録処理を実行し、エラーが出るような仕組みを入れます。
Todo
にバリデーションエラーのフィールドを追加して、以下のような値がレスポンスに含まれるようにしました。
"errors":[{"field":"name","error":"blank"}]
また、処理の結果が正常に行われたかのresult
フィールド(Boolean)を追加しました。
result
がtrue
なら一覧をリロード、false
ならエラー情報をalert
で表示する仕組みを作ってみます。
const ADD_TODO = gql`
mutation addTodo($name: String!) {
addTodo(input: { name: $name }) {
todo {
id
name
+ errors {
+ field
+ error
+ }
}
+ result
}
}
`;
const [addTodo] = useMutation(ADD_TODO, {
- update() {
- refetch();
- },
- });
+ update(
+ _cache,
+ {
+ data: {
+ addTodo: {
+ todo: { errors },
+ result,
+ },
+ },
+ }
+ ) {
+ if (result) {
+ refetch();
+ } else {
+ errors.forEach(({ field, error }) => {
+ alert(`${field} ${error}`);
+ });
+ }
+ },
+ });
errors
の型が宣言されていないので、エラーが発生しました。
型宣言をしてあげます。
type ValidationErrorType = {
field: string;
error: string;
};
-errors.forEach(({ field, error }) => {
+errors.forEach(({ field, error }: ValidationErrorType) => {
alert(`${field} ${error}`);
});
エラーは解消され、バリデーションエラーメッセージがalert
で確認できるようになりました。
前回同様、graphql-code-generator
を使って、綺麗にしていこうと思います。
graphql-code-generatorを使う
まずは設定から
queries
とは別にmutations
用のディレクトリを作成して、今回作成したクエリは、そちらに格納します。
-documents: ./graphql/queries/*.graphql
+documents:
+ - ./graphql/mutations/*.graphql
+ - ./graphql/queries/*.graphql
mutation addTodo($name: String!) {
addTodo(input: { name: $name }) {
todo {
id
name
errors {
field
error
}
}
result
}
}
これでyarn generate
を実行すると、src/types.d.ts
にTODOを追加する用のuseAddTodoMutation
が用意されました。
useAddTodoMutation
を使って追加処理を書き直してみます。
-import { useTodosQuery } from "./types.d";
+import { useTodosQuery, useAddTodoMutation } from "./types.d";
+const [addTodo] = useMutation(ADD_TODO, {
+const [addTodo] = useAddTodoMutation({
ん?エラーが起きました。
どうやらdata
はnull
かundefined
の可能性があるそうです。
それらを考慮して、少し書き直します。
const [addTodo] = useAddTodoMutation({
update(_cache, { data }) {
const result = data?.addTodo?.result || false;
const errors = data?.addTodo?.todo.errors || [];
if (result) {
refetch();
} else {
errors.forEach((e) => {
if (e) alert(`${e.field} ${e.error}`);
});
}
},
});
少し手直しが必要でしたが、クエリと、型宣言の用意が不要になったので、ソース的にはスッキリしました
同様に削除処理を実装してみる
雑ですが、各TODOの隣に、削除ボタンを用意して、削除処理を実行できるようにします。
mutation delTodo($id: ID!) {
delTodo(input: { id: $id }) {
todo {
id
}
}
}
削除対象のTODOを特定するために、IDを取得する必要があります。
graphql/queries/todos.graphql
では、TODOのname
だけ取得しているので、id
もとるように修正します。
query todos {
todos {
+ id
name
}
}
yarn generate
を実行するとuseDelTodoMutation
関数がsrc/types.d.ts
に生まれました。
このuseDelTodoMutation
を使って、削除
ボタンクリック時に削除処理が実行できるようにします。
-import { useTodosQuery, useAddTodoMutation } from "./types.d";
+import { useTodosQuery, useAddTodoMutation, useDelTodoMutation } from "./types.d";
+const [delTodo] = useDelTodoMutation({
+ update() {
+ refetch();
+ }
+});
-{data && data.todos.map(({ name }, i) => <li key={i}>{name}</li>)}
+{data && data.todos.map(({ id, name }, i) => <li key={i}>{name}<button onClick={() => delTodo({ variables: { id } })}>削除</button></li>)}
お手軽に削除処理が実装できました
最後に
一つの画面、コンポーネントで、一覧取得、追加処理、削除処理を実装したので、少しボリュームの大きめなファイルとなってしまったかもしれません。
別のファイルに型定義やクエリ情報を定義して、import
でも良いかもしれないですが、人力で且つ、複数人となると、管理が次第に煩雑になったりする可能性があると思います。
これが、ある意味graphql-code-generator
のルールに則って開発をしていると、その問題が解消されるのではないかと思いました。
今回は、かなり小規模な例として、実装したので、導入にあたって障害となる箇所をあまり感じられませんでした。
LGTM なツールと思います