Archived Build a Blog Site with Next.js and Firebase Part 5 - Editing & Deleting Posts
Archived
🚨 ATTENTION 🚨
You are currently viewing an archived post. The information here may no longer be accurate or maybe Ashlee just decided not to publish the post as publicly anymore.
Welcome to the fifth and final post in my new “Build a Blog Site with Next.js and Firebase” series! This series is pretty similar to a series I wrote in 2019: “Build a React & Firebase Blog Site”. Because it’s been well over a year since I published that series, I decided to create a new series and use the Next.js React framework this time. It’s a fun framework to use, and I know so many people that are curious about it. I hope you have enjoyed the series! As always, you can contact me through Twitter or email if you run into any issues.
- Read Part 1 of the series
- Read Part 2 of the series
- Read Part 3 of the series
- Read Part 4 of the series
Add a Page for Editing Posts
- Add a new
updatePost
function tolib/firebase.js
:
/*
Updates the data for the given post in the database.
*/
export const updatePost = async (post) => {
initFirebase();
return firebase.database().ref(`/posts/${post.slug}`).set(post);
};
- Add
edit.module.scss
to thestyles
directory:
.EditPage {
form {
h1 {
margin-top: 0;
margin-bottom: 24px;
}
input {
margin-bottom: 16px;
}
textarea {
height: 300px;
}
button {
display: block;
margin-right: 0;
margin-left: auto;
margin-top: 24px;
}
}
}
- Add an
edit
directory topages
. - Add
[slug].js
topages/edit
directory:
import { useState } from 'react';
import { useRouter } from 'next/router';
import { getPostBySlug, updatePost } from '@lib/firebase';
import { useAuth } from '@contexts/auth';
import { Layout } from '@components';
import styles from '@styles/edit.module.scss';
const EditPage = ({ post }) => {
const router = useRouter();
const [user, userLoading] = useAuth();
const [values, setValues] = useState(post);
const [isLoading, setIsLoading] = useState(false);
if (userLoading) {
return null;
}
if (!user && typeof window !== 'undefined') {
router.push('/signin');
return null;
}
const handleChange = (e) => {
const id = e.target.id;
const newValue = e.target.value;
setValues({ ...values, [id]: newValue });
};
const handleSubmit = (e) => {
e.preventDefault();
let missingValues = [];
Object.entries(values).forEach(([key, value]) => {
if (!value) {
missingValues.push(key);
}
});
if (missingValues.length > 1) {
alert(`You're missing these fields: ${missingValues.join(', ')}`);
return;
}
setIsLoading(true);
updatePost(values)
.then(() => {
setIsLoading(false);
router.push(`/post/${post.slug}`);
})
.catch((err) => {
alert(err);
setIsLoading(false);
});
};
return (
<Layout>
<div className={styles.EditPage}>
<form onSubmit={handleSubmit}>
<h1>Edit Post: {post.slug}</h1>
<div>
<label htmlFor="title">Title</label>
<input
id="title"
type="text"
value={values.title}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="coverImage">Cover Image URL</label>
<input
id="coverImage"
type="text"
value={values.coverImage}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="coverImageAlt">Cover Image Alt</label>
<input
id="coverImageAlt"
type="text"
value={values.coverImageAlt}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="content">Content</label>
<textarea
id="content"
value={values.content}
onChange={handleChange}
/>
</div>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Updating...' : 'Update'}
</button>
</form>
</div>
</Layout>
);
};
export async function getServerSideProps(context) {
const post = await getPostBySlug(context.query.slug);
return {
props: {
post,
},
};
}
export default EditPage;
- Go to
http://localhost:3000/edit/my-first-blog-post
in your browser. It should look a lot like the create page. - Change something about the post and click “Update”.
- You should be taken to the page for the post and should see your changes.
- Commit and push your work to your repository:
git add .
git commit -m "Adding EditPage component"
git push
Install FontAwesome
- Install the
@fortawesome/fontawesome-svg-core
,@fortawesome/free-solid-svg-icons
, and@fortawesome/react-fontawesome
packages:
npm install @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons @fortawesome/react-fontawesome
- Load the icons in
pages/_app.js
:
import { library } from '@fortawesome/fontawesome-svg-core';
import { fas } from '@fortawesome/free-solid-svg-icons';
library.add(fas);
- Commit and push your work to your repository:
git add .
git commit -m "Installing FontAwesome"
git push
Add an Icon component
We’re going to make a wrapper component for the FontAwesomeIcon
to make it easy to render an icon at different sizes and so we don’t have to remember the import path for the FontAwesomeIcon
component. The classnames
package is great conditionally applying classes based on boolean props passed to a component. You can read more about the package here.
- Install the
classnames
package:
npm install classnames
- Add
Icon
directory to thecomponents
directory. - Add
Icon.module.scss
to thecomponents/Icon
directory:
$icon-small: 1rem;
$icon-medium: 1.5rem;
$icon-large: 2rem;
.Icon {
width: $icon-medium;
height: $icon-medium;
font-size: $icon-medium;
&--small {
width: $icon-small;
height: $icon-small;
font-size: $icon-small;
}
&--large {
width: $icon-large;
height: $icon-large;
font-size: $icon-large;
}
}
- Add
Icon.jsx
to thecomponents/Icon
directory:
import classNames from 'classnames/bind';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import styles from './Icon.module.scss';
const Icon = ({ name, small, large }) => {
let cx = classNames.bind(styles);
let className = cx('Icon', {
'Icon--small': small,
'Icon--large': large,
});
return <FontAwesomeIcon className={className} icon={name} />;
};
export default Icon;
- Update
components/index.js
to exportIcon
:
export { default as Icon } from './Icon/Icon';
- Commit and push your work to your repository:
git add .
git commit -m "Adding Icon component"
git push
Add a Link for EditPage
- Import the new
Icon
component inPostPage
(that’spages/post/[slug.js]
):
import { Icon, Layout } from '@components';
- Also import
useAuth
inPostPage
:
import { useAuth } from '@contexts/auth';
- Invoke
useAuth
afteruseRouter
inPostPage
:
const [user] = useAuth();
- Wrap the
<h1>
with the post’s title in a<div>
and also render an edit link in the<div>
if there’s an authenticated user:
<div>
<h1>{post.title}</h1>
{user && (
<a href={`/edit/${post.slug}`}>
<Icon name="pencil-alt" />
</a>
)}
</div>
- After the
<h1>
styles instyles/post.module.scss
, add styles for the<a>
element:
a {
padding: 8px;
color: black;
}
- Go to
http://localhost:3000/post/my-first-blog-post
in your browser. - Click the new pencil icon.
- You should be taken to the edit page for
my-first-blog-post
. - Commit and push your work to your repository:
git add .
git commit -m "Adding edit link"
git push
Add a Delete Button
- Add a new
deletePost
function inlib/firebase.js
:
/*
Deletes a post from the database.
*/
export const deletePost = async (slug) => {
initFirebase();
return firebase.database().ref(`/posts/${slug}`).set(null);
};
- Import the
deletePost
function inPostPage
:
import { deletePost, getPostBySlug } from '@lib/firebase';
- At the same level as the edit link we just added in the previous section, add a
<button>
and wrap both elements in adiv
:
<span>
<a href={`/edit/${post.slug}`}>
<Icon name="pencil-alt" />
</a>
<button
onClick={() => {
const shouldDeletePost = confirm(
'Are you sure you want to delete this post?',
);
if (shouldDeletePost) {
deletePost(post.slug).then(() => {
router.push('/');
});
}
}}
>
<Icon name="trash-alt" />
</button>
</span>
- Replace the
a
styles you added tostyles/post.module.scss
with these:
& > div {
display: flex;
margin: 0;
a {
padding: 8px;
color: black;
}
button {
background: transparent;
box-shadow: none;
border: none;
color: black;
}
}
- Create a new post for testing the delete functionality.
- Go to the post’s page and you should see a trashcan icon to the right of the pencil icon like this:
- Click the trashcan icon and you should see an alert that says “Are you sure you want to delete this post?”
- Click OK and you should be taken back to the home page. That post will no longer be listed and it should also be gone from the database.
- Commit and push your work to your repository:
git add .
git commit -m "Adding delete button"
git push
- Celebrate!!! You did it!!! 🎉