こんにちは、KOUKIです。Reactで画面開発を行なっています。
前回は、Productページを実装しました。今回は、Productを作成する機能を作っていきたいと思います。
尚、「React, NextJS and Golang: A Rapid Guide – Advanced」コースを参考にしています。解釈は私が勝手に付けているので、本物をみたい場合は受講をお勧めします!
<目次>
前回
事前準備
フォルダ/ファイル
1 |
touch react-admin/src/pages/products/ProductForm.tsx |
Productの作成
Addボタンをつける
前回作成したProductページ上に、Addボタンをつけましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// pages/products/Products.tsx ... import { Button, ... } from '@material-ui/core' ... const Products = () => { ... return ( <Layout> <div className="pt-3 pb-2 mb-3 border-bottom"> <Button href="/products/create" variant="contained" color="primary" > Add </Button> </div> ... </Layout> |
「”/products/create”」は、これから実装するFormページへのパスです。
ブラウザから「http://localhost:3000/products」へアクセスして、確認してみましょう。

Formページの作成
次は、Formページを作ります。
1 2 3 4 5 6 7 8 9 10 |
// pages/products/ProductForm.tsx import Layout from '../../components/Layout' const ProductForm = () => { return( <Layout> Product Form </Layout> ) } export default ProductForm |
ルートの設定
Formページへのルートを実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// App.tsx .... import ProductForm from './pages/products/ProductForm' function App() { return ( <div className="App"> <BrowserRouter> ... <Route path={'/products'} exact component={Products}></Route> <Route path={'/products/create'} component={ProductForm}></Route> </BrowserRouter> </div> ); } export default App; |
ページ遷移の確認
Addボタンを押下して、Formページに遷移するか確認しましょう。

OKですね。
Formの作成
次に、Product情報を送信するFormを実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
// pages/products/ProductForm.tsx import { useState, useEffect } from 'react' import { Button, TextField } from '@material-ui/core' import Layout from '../../components/Layout' const ProductForm = () => { // フォームの状態管理 const [title, setTitle] = useState('') const [description, setDescription] = useState('') const [image, setImage] = useState('') const [price, setPrice] = useState(0) return( <Layout> <form> <div className="mb-3"> <TextField label="title" onChange={e => setTitle(e.target.value)} /> </div> <div className="mb-3"> <TextField label="Description" rows={4} multiline onChange={e => setDescription(e.target.value)} /> </div> <div className="mb-3"> <TextField label="Image" onChange={e => setImage(e.target.value)} /> </div> <div className="mb-3"> <TextField label="Price" type="number" onChange={e => Number(e.target.value))} /> </div> <Button variant="contained" color="primary" type="submit"> Submit </Button> </form> </Layout> ) } export default ProductForm |
Submit処理の実装
APIへProduct情報を送信する処理を実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
// pages/products/ProductForm.tsx import { useState, SyntheticEvent } from 'react' import { Button, TextField } from '@material-ui/core' import Layout from '../../components/Layout' import axios from 'axios' import { Redirect } from 'react-router-dom' const ProductForm = () => { ... const [redirect, setRedirect] = useState(false) // リクエスト先 const productCreateUrl = 'products' // SyntheticEvent // https://ja.reactjs.org/docs/events.html const submit = async (e: SyntheticEvent) => { e.preventDefault() await axios.post(productCreateUrl, { title, description, image, price }) setRedirect(true) } if (redirect) { return <Redirect to={'/' + productCreateUrl} /> } return(...) } export default ProductForm |
作成確認
Formページから商品情報をAPIへ送信し、問題なく登録されるか確認しましょう。
以下の画像では、「Harry Potter」を送信します。

送信が問題なく完了したら、Productページへリダイレクトされるので、商品が登録されているか確認しましょう。

No.31に登録されていますね! incredibleがimcredibleになってしまってますが^^;
次回
次回は、Productの更新機能を実装します。
Reactまとめ
参考書籍
ソースコード
ここまで実装したソースコードを下記に記載します。
Products.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
// pages/products/Products.tsx import { useState, useEffect } from 'react' import { Product } from '../../models/Product' import axios from 'axios' import { Button, Table, TableBody, TableRow, TableHead, TableCell, TableFooter, TablePagination } from '@material-ui/core' import Layout from '../../components/Layout' const Products = () => { const [products, setProducts] = useState<Product[]>([]) const productsUrl = '/products' const [page, setPage] = useState(0) const perPage = 10 useEffect(() => { ( async () => { const { data } = await axios.get(productsUrl) setProducts(data) } )() }, []) const del = async (id: number) => { if (window.confirm('商品を削除しますか?')) { await axios.delete(`${productsUrl}/${id}`) setProducts(products.filter(p => p.id !== id)) } } return ( <Layout> <div className="pt-3 pb-2 mb-3 border-bottom"> <Button href="/products/create" variant="contained" color="primary" > Add </Button> </div> <Table className="table table-striped table-sm"> <TableHead> <TableRow> <TableCell>#</TableCell> <TableCell>Image</TableCell> <TableCell>Title</TableCell> <TableCell>Description</TableCell> <TableCell>Price</TableCell> <TableCell>Actions</TableCell> </TableRow> </TableHead> <TableBody> {products.slice(page * perPage, (page + 1) * perPage).map(product => { return ( <TableRow key={product.id}> <TableCell>{product.id}</TableCell> <TableCell><img src={product.image} width={50}/></TableCell> <TableCell>{product.title}</TableCell> <TableCell>{product.description}</TableCell> <TableCell>{product.price}</TableCell> <TableCell> <Button variant="contained" color="secondary" onClick ={() => del(product.id)} >Delete</Button> </TableCell> </TableRow> ) })} </TableBody> <TableFooter> <TableRow> <TablePagination count={products.length} page={page} onChangePage={(e, newPage) => setPage(newPage)} rowsPerPageOptions={[]} rowsPerPage={perPage} ></TablePagination> </TableRow> </TableFooter> </Table> </Layout> ) } export default Products |
App.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// App.tsx import './App.css'; import {BrowserRouter, Route} from 'react-router-dom' import {RedirectToUsers} from './components/RedirectToUsers' import Users from './pages/Users' import Login from './pages/Login' import Register from './pages/Register' import Links from './pages/Links' import Products from './pages/products/Products' import ProductForm from './pages/products/ProductForm' function App() { return ( <div className="App"> <BrowserRouter> <Route path={'/'} exact component={RedirectToUsers}></Route> <Route path={'/login'} component={Login}></Route> <Route path={'/register'} component={Register}></Route> <Route path={'/users'} exact component={Users}></Route> <Route path={'/users/:id/links'} component={Links}></Route> <Route path={'/products'} exact component={Products}></Route> <Route path={'/products/create'} component={ProductForm}></Route> </BrowserRouter> </div> ); } export default App; |
ProductForm.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
// pages/products/ProductForm.tsx import { useState, SyntheticEvent } from 'react' import { Button, TextField } from '@material-ui/core' import Layout from '../../components/Layout' import axios from 'axios' import { Redirect } from 'react-router-dom' const ProductForm = () => { const [title, setTitle] = useState('') const [description, setDescription] = useState('') const [image, setImage] = useState('') const [price, setPrice] = useState(0) const [redirect, setRedirect] = useState(false) // リクエスト先 const productCreateUrl = 'products' // SyntheticEvent // https://ja.reactjs.org/docs/events.html const submit = async (e: SyntheticEvent) => { e.preventDefault() await axios.post(productCreateUrl, { title, description, image, price }) setRedirect(true) } if (redirect) { return <Redirect to={'/' + productCreateUrl} /> } return( <Layout> <form onSubmit={submit}> <div className="mb-3"> <TextField label="title" onChange={e => setTitle(e.target.value)} /> </div> <div className="mb-3"> <TextField label="Description" rows={4} multiline onChange={e => setDescription(e.target.value)} /> </div> <div className="mb-3"> <TextField label="Image" onChange={e => setImage(e.target.value)} /> </div> <div className="mb-3"> <TextField label="Price" type="number" onChange={e => setPrice(Number(e.target.value))} /> </div> <Button variant="contained" color="primary" type="submit"> Submit </Button> </form> </Layout> ) } export default ProductForm |
コメントを残す
コメントを投稿するにはログインしてください。