I am creating a front end app with NextJS 13 and fetching data from JSON placeholder. As the API docs say, data does not persists on the server.
My issue is that i am "creating" a new post which is being faked by the api so i cannot update it with a PUT request as the other posts that i am fetching from the API. Or at least that is what i understand.
So how can i update my recently "created" post?
This is my List of Posts (ListOfPosts.jsx)
'use client'
import { useEffect, useState } from "react";
import Link from "next/link";
import axios from "axios";
import PostItem from "./PostItem";
export default function ListOfPosts() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [newPostTitle, setNewPostTitle] = useState('');
const [newPostBody, setNewPostBody] = useState('');
useEffect(() => {
const fetchPosts = async () => {
try {
const response = await axios.get(
'https://jsonplaceholder.typicode.com/posts'
);
setPosts(response.data);
setLoading(false);
} catch (error) {
console.log(error);
}
};
fetchPosts();
}, []);
const addPost = async (e) => {
e.preventDefault();
try {
const response = await axios.post(
'https://jsonplaceholder.typicode.com/posts',
{
title: newPostTitle,
body: newPostBody,
userId: 1,
}
);
const newPost = response.data;
console.log('Response from creating post:', response);
console.log('New post created:', newPost);
setPosts((prevPosts) => [...prevPosts, newPost]);
// Reset form inputs
setNewPostTitle('');
setNewPostBody('');
} catch (error) {
console.error('Error creating post:', error);
}
};
const updatePost = async (postId, updatedTitle, updatedBody) =>{
console.log('Updating post with ID:', postId);
const res = await axios.put(`https://jsonplaceholder.typicode.com/posts/${postId}`,{
id: postId,
title: updatedTitle,
body: updatedBody,
userId: 1
})
setPosts((prevPosts) =>
prevPosts.map((post) =>
post.id === postId ? { ...post, title: updatedTitle, body: updatedBody } : post
)
);
}
if(loading){
return <h1>Loading...</h1>
}
return (
<div className="flex flex-col gap-4 items-center">
<div className="">
<h2>Create New Post</h2>
<form onSubmit={addPost}>
<div>
<label htmlFor="title">Title:</label>
<input
name="title"
type="text"
id="title"
value={newPostTitle}
onChange={(e) => setNewPostTitle(e.target.value)}
required
className="rounded text-black bg-transparent"
/>
</div>
<div>
<label htmlFor="body">Body:</label>
<input
type="text"
id="body"
value={newPostBody}
onChange={(e) => setNewPostBody(e.target.value)}
required
className="rounded bg-transparent text-black"
/>
</div>
<button type="submit">Create Post</button>
</form>
</div>
<div className="flex flex-col gap-4 w-full items-center ">
{posts.map((post) => (
<PostItem key={post.id} post={post} updatePost={updatePost} />
))}
</div>
</div>
);
}
And this is my PostItem.jsx
'use client'
import React, { useState } from 'react'
import { GiCancel } from 'react-icons/gi';
import { RiSave3Fill } from 'react-icons/ri';
import { GrEdit } from 'react-icons/gr';
function PostItem({ post, updatePost }) {
const [ isEditing, setIsEditing ] = useState(false);
const [updatedTitle, setUpdatedTitle] = useState(post.title);
const [updatedBody, setUpdatedBody] = useState(post.body);
if (!post) {
return null;
}
const handleEdit = (postId) => {
setIsEditing(true);
}
const handleCancelEdit = (postId) => {
setIsEditing(false);
}
const handleSave = (postId) => {
updatePost(postId, updatedTitle, updatedBody);
setIsEditing(false);
};
const handleClick = (postId) => {
console.log(postId);
}
return (
<article onClick={() => handleClick(post.id)} className="relative flex flex-col gap-2 border-9 border-black-1000 rounded-2xl bg-green-950 mt-6 w-96 h-auto p-4 overflow-hidden shadow-3xl text-black">
{isEditing ? (
<div>
<input
className='bg-transparent text-black border-b-2 rounded-sm border-black uppercase mb-4'
type="text"
value={updatedTitle}
onChange={(e) => setUpdatedTitle(e.target.value)}
/>
<textarea
className='bg-transparent border-2 border-black p-2 text-black w-full h-auto overflow-hidden rounded-lg'
value={updatedBody}
onChange={(e) => setUpdatedBody(e.target.value)}
/>
<button
className='border-9 rounded-xl border-black-1000 bg-blue-600 text-black p-2 shadow-2xl w-10 absolute top-2 right-16'
onClick={() => handleSave(post.id)}
>
<RiSave3Fill className='w-full h-full'/>
</button>
<button
className='border-9 rounded-xl border-black-1000 bg-red-700 text-black p-2 shadow-2xl w-10 absolute top-2 right-2'
onClick={() => handleCancelEdit(post.id)}
>
<GiCancel/>
</button>
</div>
) : (
<div>
<h2 className="text-xl font-bold text-white uppercase">
{post.id} {post.title}
</h2>
<p className="my-4 text-gray-950 ">{post.body}</p>
<button
className='border-9 rounded-xl border-black-1000 bg-yellow-500 text-black p-2 shadow-2xl w-10 absolute bottom-2 right-2'
onClick={() => handleEdit(post.id)} >
<GrEdit/>
</button>
</div>
)}
</article>
);
}
export default PostItem
I am using the App Router directory from NextJs 13.