I am working on a commenting feature for a blog using latest version of NextJs.
Text input collects data, and sends it to the database ‘Vercel` hosted,
and I fetch the data from the frontend successfully as expected.
However, displaying the data in the frontend behaves as expected, but if user
refreshes the page the data gets lost for some reasons (pls see attached gif) – This behavior is not
expected as data is not persistent. Is this because this is a dynamic route in a page route
app or perhaps the comments are displayed in a component
rather than the actual page – Can you help figure this out? Thanks in advance.
//Dynamic Route '/pages/blog/[slug].js'
import React, { useRef, useState, useEffect } from 'react';
import { getSinglePost, getPosts } from '../../lib/posts';
import { Button } from '@nextui-org/react';
import CommentBox from '../../components/Blogs/CommentBox';
import useSWR from 'swr';
import { useSession } from "next-auth/react";
const PostPage = ({ post }) => {
const [postContent, setPostContent] = useState({
id: post.id,
title: post.title,
content: post.html
});
const [comments, setComments] = useState([]);
const [newComment, setNewComment] = useState('');
const [value, setValue] = useState(0);
const { data: session } = useSession();
const handleCommentChange = (event) => {
setNewComment(event.target.value);
};
//Access post.id and sets it as postId, and the using `SWR` to fetch data from the database
const postId = post.id;
const { data: commentsData, error } = useSWR(`/api/blog/commentsystem?postId=${postId}`);
// Update comments state when data is fetched
useEffect(() => {
if (commentsData) {
setComments(commentsData);
}
}, [commentsData]);
// Function to handle form submission
const handleSubmit = async (event) => {
event.preventDefault();
if (newComment.trim() !== '' && session) {
setLoading(true);
const user = session.user;
const commentObject = {
articleContent: newComment,
user: user,
date: new Date().toLocaleString(),
};
const { user: articleUser, name: articleEmail } = user;
const title = post.title;
try {
const response = await fetch('/api/blog/commentsystem', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ user: articleUser, articleContent: newComment, email: articleEmail, postTitle: title, postId: postId })
});
if (!response.ok) {
throw new Error('Failed to post comment');
}
const responseData = await response.json();
console.log("data from DB:", responseData); // returns data as expected.
setComments(prevComments => [...prevComments, responseData]);
setNewComment('');
setLoading(false);
} catch (error) {
console.error('Error posting comment:', error.message);
}
}
};
console.log("Comments data from DB:", comments); // returns data as expected.
return (
<div>
<span >
{/* Component that displays comments takes in data set as state variable as a prop*/}
< CommentBox comments={comments} />
</span>
<form onSubmit={handleSubmit}>
<div >
<div >
<label htmlFor="comment" >Share your thoughts</label>
<textarea
id="comment"
rows="4"
placeholder="Post comments..."
value={newComment}
onChange={handleCommentChange}
required
/>
</div>
<div >
<button type="submit">
Post comment
</button>
</div>
</div>
</form>
</div>
);
};
//Function below gets 'post' from external api, and sets it as props
export async function getStaticPaths() {
const allPosts = await getPosts();
const paths = allPosts.map((post) => ({
params: { slug: post.slug }
}));
return {
paths,
fallback: false
};
}
export async function getStaticProps({ params }) {
const post = await getSinglePost(params.slug);
if (!post) {
return {
notFound: true
};
}
return {
props: { post }
};
}
export async function generateMetadata({ params }) {
const post = await getSinglePost(params.slug);
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [
{
url: post.image,
},
],
},
};
}
export default PostPage;
///////////////////////////////////
// Component
///////////////////////////////////
const CommentBox = ({ comments }) => {
//comments is imported from the dynamic page as prop
console.log(comments); // returns data as expected.
return (
<div>
<div>
{comments && comments.map((item, index) => {
return (
<div key={item.id}>
{Array.isArray(item.comments) && item.comments.map((comment, commentIndex) => (
<div key={comment.id}>
{/* Render each comment */}
{console.log(comment)}
<div>
{/* Render profile image */}
<div>
{/* Render comment author */}
<div>
<span>{comment.commentBy}</span>
</div>
{/* Render comment content */}
<div>
<p>{comment.comment}</p>
</div>
{/* Render posted time */}
</div>
</div>
</div>
))}
</div>
);
})}
</div>
</div>
);
};
export default CommentBox;
3
Answers
You can use local storage.
What is local storage?
Local storage is a form of data persistence that allows you store data locally on your computer while having access to those resources offline. Local storage can store large amounts of data locally without having to transfer data to a server. A benefit of using local storage is that it doesn’t have expiration date, implying that stored data can be available anytime and anywhere.
You can create something like that:
Then when it loads you will use
localStorage.get
to get the data.I would suggest you to use Cache API.
Compared to other traditional storage mechanisms like localstorage and sessionstorage it has many advantages. I am adding a couple of them below
If you are aiming towards an offline experience as well for your app, then you can integrate a ServiceWorker too in future. Cache API is the suggested storage mechanism to be used along with a Service Worker
The answer actually depends on what you want to store and the size of the same payload. Upon reading through, i anticipate you would want to retain the items or inputs made by an user and retrieve the same set once the page loads/refreshes back. There are two ways to do:
Redis
orMemcached
to store much bigger data sizes.