前回の簡易PostアプリをSSR対応してみました。
大きな違いは、API Routes の /api/notes/route.tsが不要になります。
これにより呼び出しのfetchも不要になり、その代わりにServerActionのaserver.tsにDB保存、削除を記述します。
(DB取得は、前回テストでpage.tsxにprismaのデータ取得を入れたものを使用し、それをAppに渡します。 page.tsx <App data={data} /> )
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 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 |
'use client'; import { useState, useEffect, ChangeEvent } from 'react'; import { Card, Row, Col, Input, Button, Table } from 'antd'; import { ColumnsType } from 'antd/es/table'; import { notes } from '@prisma/client'; import { saveAction, deleteAction } from './server' interface DataType { key: string; id: number; content: string; createdAt: string; } export default function App(props:{data: any}) { const [content, setContent] = useState(''); const [dataSource, setDataSource] = useState<DataType[]>([]); useEffect(()=>{ console.log('props' , props.data) setDataSource(props.data) }, [props.data]) console.log('win', typeof window !== "undefined") const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => { setContent(event.target.value); }; const columns: ColumnsType<DataType> = [ { title: 'created_at', dataIndex: 'createdAt', width: '20%', render: (date: Date) => new Date(date).toLocaleDateString(), sorter: (a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt), }, { title: 'content', dataIndex: 'content', width: '75%', }, { width: '5%', render: (record: DataType) => ( <form action={deleteAction}> {/* <Button danger onClick={() => handleDeleteClick(record.id)}> Delete </Button> */} <input type="hidden" name="id" value={record.id} /> <button type="submit">Delete</button> </form> ), }, ]; const centeredStyle: React.CSSProperties = { display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh', }; return ( <div style={centeredStyle}> <Card title='Note' style={{ width: 800 }}> <form action={saveAction}> <Row> <Col span={16}> <input type="text" value={content} name="content" onChange={handleInputChange} /> </Col> <Col span={7} offset={1}> <button type="submit">Save</button> </Col> </Row> </form> <Table dataSource={dataSource} columns={columns} rowKey={(record) => record.id} pagination={{ pageSize: 5, }} style={{ marginTop: 20 }} /> </Card> </div> ); } |
server.ts
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 |
"use server" import { revalidatePath } from 'next/cache' import { PrismaClient } from '@prisma/client' import { redirect } from 'next/navigation' const prisma = new PrismaClient() export async function saveAction(fd: FormData) { console.log(fd) await prisma.notes.create({ data:{ content: fd.get('content') as string } }) redirect("/"); } export async function deleteAction(fd: FormData) { console.log(fd) await prisma.notes.delete({ where:{ id: Number(fd.get('id')) } }) redirect("/"); } |
ボタンデザインは標準のものになります。
fetchとそれを待ち受けするroute.tsのハンドラーの部分が不要になりまるため、コードばシンプルになります。SSRについてパフォーマンスのメリットがよく言われますが、私はこのサーバサイドとクライアントサイドのデータ渡しがネットワークを意識しなくても良い点が一番のメリットと思っています。
といっても、ServerActionで使われている、Form Submit Action は、Webの初期の機能であり、新しいのか古いのかわからない感じが面白いです。
まぁ、昔はJavaScriptが走らないブラウザでしたので、Server Side Rendering が普通でした。この点、PHPで開発していたFacebookが、Reactでこのようなアプローチを取るのも自然に感じます。
PHPで、JavaScriptコードを動的に書きブラウザで実行させることをAJAX以前にやっていたこともありますが、現在のSSRを学ぶ上でとても参考になっています。
Nextjsは、サーバとブラウザで同じ言語が使えるメリットを最大限活かしていると実感します。