アイテムページ
編集ページの作成
- mkdir pages/item/update
- touch pages/item/update/[id].js
pages/item/[id].jsは既に存在するのでpages/item/update/[id].jsとする。
import { useState } from 'react'
const UpdateItem = (props) => {
const [title, setTitle] = useState(props.item.title);
const [description, setDescription] = useState(props.item.description);
const [price, setPrice] = useState(props.item.price);
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await fetch(`http://localhost:3000/api/item/update/${props.item._id}`, {
method: 'POST',
headers: {
'Accrpt': 'application/json',
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`
},
body: JSON.stringify({
title: title,
description: description,
price: price,
})
});
const json = await response.json();
alert(json.msg);
} catch (err) {
alert('更新失敗');
}
};
return (
<form onSubmit={handleSubmit}>
<h1>アイテム作成</h1>
<p>title: <input type="text" name="title" value={title} onChange={e=>setTitle(e.target.value)} required/></p>
<p>description: <input type="text" name="description" value={description} onChange={e=>setDescription(e.target.value)}/></p>
<p>price: <input type="text" name="price" value={price} onChange={e=>setPrice(e.target.value)}/></p>
<p><button type="submit">保存</button></p>
</form>
)
};
export default UpdateItem;
export const getServerSideProps = async (context) => {
const response = await fetch(`http://localhost:3000/api/item/${context.query.id}`);
const item = await response.json();
return {
props: item
}
};
ログイン状態をチェックするためのカスタムフック
カスタムフックの作成
- touch utils/useAuth.js
- このように複数のコンポーネントで使う共通処理をuseXXXとして定義したものをカスタムフックと呼ぶ
- jsonwebtoken9.0.0はブラウザではverifyでエラーとなるため、8.5.1へのダウングレードが必要
npm remove jsonwebtoken
npm install jsonwebtoken@8.5.1
- トークンから取り出したemailを呼び出し元に返すためにステートを使う。
- 呼び出された際はuseEffectが実行されていないので空文字がreturnされるが、その直後にuseEffectが実行されemailが更新されることで再レンダリングされる、
※ Reactではstateを更新すると再レンダリングが発生する
- トークンがない・正しくない場合はuseRouter()のpushメソッドを使ってログインページにリダイレクトさせる
import {useState, useEffect} from 'react'
import {useRouter} from 'next/router'
import jwt from "jsonwebtoken";
const useAuth = () => {
const [email, setEmail] = useState('');
const router = useRouter();
useEffect(() => {
const token = localStorage.getItem('token');
if (!token) {
router.push('/user/login');
}
try {
const decoded = jwt.verify(token, 'hogehogehoge');
setEmail(decoded.email);
} catch (err) {
console.log(err)
router.push('/user/login');
}
}, [router]);
return email;
};
export default useAuth;
カスタムフックの使用
作成ページ
import { useState } from 'react'
import useAuth from "../../utils/useAuth"; // 追加
const CreateItem = () => {
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [price, setPrice] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await fetch('http://localhost:3000/api/item/create', {
method: 'POST',
headers: {
'Accrpt': 'application/json',
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`
},
body: JSON.stringify({
title: title,
description: description,
price: price,
})
});
const json = await response.json();
alert(json.msg);
} catch (err) {
alert('登録失敗');
}
};
const email = useAuth(); // 追加
if (email) {
return (
<form onSubmit={handleSubmit}>
<h1>アイテム作成</h1>
<p>title: <input type="text" name="title" value={title} onChange={e => setTitle(e.target.value)} required/></p>
<p>description: <input type="text" name="description" value={description}
onChange={e => setDescription(e.target.value)}/></p>
<p>price: <input type="text" name="price" value={price} onChange={e => setPrice(e.target.value)}/></p>
<p>
<button type="submit">投稿</button>
</p>
</form>
)
}
};
export default CreateItem;
編集ページ
import { useState } from 'react'
import useAuth from "../../../utils/useAuth"; // 追加
const UpdateItem = (props) => {
const [title, setTitle] = useState(props.item.title);
const [description, setDescription] = useState(props.item.description);
const [price, setPrice] = useState(props.item.price);
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await fetch(`http://localhost:3000/api/item/update/${props.item._id}`, {
method: 'POST',
headers: {
'Accrpt': 'application/json',
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token')}`
},
body: JSON.stringify({
title: title,
description: description,
price: price,
})
});
const json = await response.json();
alert(json.msg);
} catch (err) {
alert('更新失敗');
}
};
const email = useAuth(); // 追加
if (email === props.item.email) { // 追加
return (
<form onSubmit={handleSubmit}>
<h1>アイテム作成</h1>
<p>title: <input type="text" name="title" value={title} onChange={e => setTitle(e.target.value)} required/></p>
<p>description: <input type="text" name="description" value={description}
onChange={e => setDescription(e.target.value)}/></p>
<p>price: <input type="text" name="price" value={price} onChange={e => setPrice(e.target.value)}/></p>
<p>
<button type="submit">保存</button>
</p>
</form>
);
}
};
export default UpdateItem;
export const getServerSideProps = async (context) => {
const response = await fetch(`http://localhost:3000/api/item/${context.query.id}`);
const item = await response.json();
return {
props: item
}
};