【GraphQL】フロントエンドでGraphQLのデータを追加する
GraphQL

【GraphQL】フロントエンドでGraphQLのデータを追加する

作成日:2021年11月28日
更新日:2021年11月29日

前回は、GraphQL で接続したデータ一覧から、詳細画面へ遷移させました。

graphql-client-detail

【GraphQL】フロントエンドで詳細画面へ遷移する

今回は、フロントエンドでデータを追加します。

コードは、こちらです。

フロントエンド:

バックエンド:

読書リスト画面で、新規作成ボタンをクリックすると、本のデータを作成できるようにします。

まずは、新規作成ボタンから出てくるモーダルを作ります。

components フォルダに、ModalBook.tsx を作成します。

モーダルの内容は、MUI の modal を参考にします。

https://mui.com/components/modal/

tsx
import * as React from "react";
import Button from "@mui/material/Button";
import Modal from "@mui/material/Modal";
export default function BasicModal() {
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<div>
<Button onClick={handleOpen} variant="contained">
新規作成
</Button>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
></Modal>
</div>
);
}

Modal の中に、フォームを作成します。

components フォルファに、AddBook.tsx を作成します。

GraphQL に接続するのが目的なので、MUI の内容は割愛して、今後ご紹介します。

tsx
import * as React from "react";
import Box from "@mui/material/Box";
import TextField from "@mui/material/TextField";
import FormControlLabel from "@mui/material/FormControlLabel";
import {
Button,
FormControl,
FormLabel,
MenuItem,
Paper,
Radio,
RadioGroup,
styled,
} from "@mui/material";
const categories = [
{
value: 1,
name: "文学",
},
{
value: 2,
name: "自己啓発",
},
];
export default function AddBook() {
const [title, setTitle] = React.useState("");
const [author, setAuthor] = React.useState("");
const [category, setCategory] = React.useState("");
const [isRead, setIsRead] = React.useState(false);
const handleChangeTitle = (event: React.ChangeEvent<HTMLInputElement>) => {
setTitle(event.target.value as string);
};
const handleChangeAuthor = (event: React.ChangeEvent<HTMLInputElement>) => {
setAuthor(event.target.value as string);
};
const handleChangeCategory = (event: React.ChangeEvent<HTMLInputElement>) => {
setCategory(event.target.value as string);
};
const handleChangeIsRead = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value === "true";
setIsRead(value);
};
const handleClick = () => {
console.log(title, author, category, isRead);
};
return (
<Box
component="form"
sx={{
"& > :not(style)": { m: 1, width: "200ch" },
}}
noValidate
autoComplete="off"
>
<CContainer>
<CPaper>
<TextField
id="title"
label="タイトル"
variant="outlined"
value={title}
onChange={handleChangeTitle}
margin="normal"
fullWidth
/>
<TextField
id="author"
label="著者"
variant="outlined"
value={author}
onChange={handleChangeAuthor}
margin="normal"
fullWidth
/>
<TextField
id="select-Category"
select
label="カテゴリ"
variant="outlined"
value={category}
onChange={handleChangeCategory}
margin="normal"
fullWidth
>
{categories.map((category) => (
<MenuItem key={category.value} value={category.value}>
{category.name}
</MenuItem>
))}
</TextField>
<Box>
<FormControl component="fieldset">
<FormLabel component="legend">読了</FormLabel>
<RadioGroup
aria-label="gender"
name="controlled-radio-buttons-group"
value={isRead}
onChange={handleChangeIsRead}
row
>
<FormControlLabel value="true" control={<Radio />} label="" />
<FormControlLabel
value="false"
control={<Radio />}
label=""
/>
</RadioGroup>
</FormControl>
</Box>
<Button variant="contained" onClick={handleClick}>
新規追加
</Button>
</CPaper>
</CContainer>
</Box>
);
}
const CContainer = styled(Box)({
display: "flex",
flexDirection: "column",
jusitifyConteng: "center",
alignItems: "center",
position: "absolute",
top: "50%",
left: "50%",
transform: "translateX(-50%) translateY(-50%)",
});
const CPaper = styled(Paper)({
padding: "2rem",
});

AddBook.tsx を ModalBook にインポートします。

tsx
import * as React from "react";
import Button from "@mui/material/Button";
import Modal from "@mui/material/Modal";
import AddBook from "./AddBook";
export default function BasicModal() {
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
return (
<div>
<Button onClick={handleOpen} variant="contained">
新規作成
</Button>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<AddBook />
</Modal>
</div>
);
}

モーダルを Book.tsx にインポートします。

tsx
import * as React from "react";
import { gql, useQuery } from "@apollo/client";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Title from "../components/Title";
import { Box, Button, Link, styled } from "@mui/material";
import ModalBook from "../components/ModalBook";
type TypeBook = {
id: number;
title: string;
author: string;
category: {
name: string;
};
isRead: boolean;
createdAt: string;
};
const booksData = gql`
query allBooks {
books {
id
title
author
category {
name
}
isRead
createdAt
}
}
`;
export default function Book() {
const { data, error, loading } = useQuery(booksData);
if (error) return <div>データを取得することができませんでした</div>;
if (loading) return <div>Loading...</div>;
const { books } = data;
return (
<React.Fragment>
<CBox>
<Title>読書リスト</Title>
<ModalBook />
</CBox>
<Table size="small">
<TableHead>
<TableRow>
<TableCell>ID</TableCell>
<TableCell>タイトル</TableCell>
<TableCell>著者</TableCell>
<TableCell>カテゴリ</TableCell>
<TableCell>読了</TableCell>
<TableCell>作成日</TableCell>
<TableCell></TableCell>
</TableRow>
</TableHead>
<TableBody>
{books.map((book: TypeBook) => (
<TableRow key={book.id}>
<TableCell>{book.id}</TableCell>
<TableCell>{book.title}</TableCell>
<TableCell>{book.author}</TableCell>
<TableCell>{book.category.name}</TableCell>
{book.isRead ? (
<TableCell></TableCell>
) : (
<TableCell></TableCell>
)}
<TableCell>
{`${new Date(Number(book.createdAt))}`
.split(" ")
.splice(1, 3)
.join(" ")}
</TableCell>
<TableCell>
<Link href={`/book/${book.id}`} underline="hover">
詳細画面
</Link>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</React.Fragment>
);
}
const CBox = styled(Box)({
display: "flex",
justifyContent: "space-between",
});

ブラウザで確認すると、

image2

image3

モーダルが完成しました。

今のところ、『新規追加』ボタンをクリックすると、console.log で GraphQL に送信したい内容が表示されます。

image4

image5

では、GraphQL に接続して、データを追加できるようにします。

まずは、カテゴリを GraphQL に接続しましょう。

CreateBook.tsx にgqlをインポートします。

tsx
import { gql } from "@apollo/client";

GraphQL を開いて、バックエンドで作成したCategoriesをコピーします。

graphql
query Categories {
categories {
id
name
}
}

コピーしたCategoriesgql内に貼り付けます。

tsx
const categoriesData = gql`
query Categories {
categories {
id
name
}
}
`;

useQuery をインポートします。

tsx
import { gql, useQuery } from "@apollo/client";

useQuery で、data、error、loading を作成します

tsx
const { data, error, loading } = useQuery(categoriesData);
if (error) return <div>データを取得することができませんでした</div>;
if (loading) return <div>Loading...</div>;
const { categories } = data;

カテゴリのフィールドを作成しましょう。

型を作成します。

また、ローカルデータのcategoriesは必要無いので、削除します。

tsx
type TypeCategory = {
id: number;
name: string;
};
tsx
const [categoryId, setCategoryId] = React.useState(0);
const handleChangeCategory = (event: React.ChangeEvent<HTMLInputElement>) => {
setCategoryId(event.target.value as unknown as number);
};
tsx
const handleChangeCategory = (event: React.ChangeEvent<HTMLInputElement>) => {
setCategoryId(event.target.value as unknown as number);
};
tsx
<TextField
id="select-Category"
select
label="カテゴリ"
variant="outlined"
value={categoryId}
onChange={handleChangeCategory}
margin="normal"
fullWidth
>
{categories.map((category: TypeCategory) => (
<MenuItem key={category.id} value={category.id}>
{category.name}
</MenuItem>
))}
</TextField>

一度、ブラウザで確認します。

image6

カテゴリが問題なく表示されています。

次は、mutation を作成します。

GraphQL を開いて、バックエンドで作成したAddBookmutationをコピーします。

graphql
mutation AddBook($input: AddBookInput!) {
addBook(input: $input) {
errors {
message
}
book {
title
author
category {
name
}
isRead
}
}
}

コピーした AddBook を gql 内に貼り付けます。

tsx
const addBook = gql`
mutation AddBook($input: AddBookInput!) {
addBook(input: $input) {
errors {
message
}
book {
title
author
category {
name
}
isRead
}
}
}
`;

useMutationをインポートします。

tsx
import { gql, useQuery, useMutation } from "@apollo/client";

データを作成するためのaddBookDataを設定します。

また、dataloadingも設定します。

tsx
const [addBookData] = useMutation(addBook);

handleClick内に、addBookDataを設定します。

追加したい項目は、title、author、categoryId、isRead なので、variablesに 4 項目を指定します。

tsx
const handleClick = () => {
addBookData({
variables: {
input: {
title,
author,
categoryId,
isRead,
},
},
});
};

一通り完成したので、ブラウザで確認します。

image7

フォーム内にデータを入力して、『新規追加』ボタンをクリックした後、リロードします。

image8

追加したデータが表示されました。

image9

GraphQL にも反映されています。

コードの全文は、こちらです。

次回は、GraphQL のデータを削除・更新します。

graphql-client-delete-update

【GraphQL】フロントエンドでGraphQLのデータを削除・更新する

© 2024あずきぱんウェブスタジオ