Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
June 22, 2021 05:36 pm GMT

How To Build Your Own Likes and Comments System With Firebase and React

One of my todo items with React apps was having performance-first dynamic comments and likes system for static websites. Why? Because it brings the capabilities of going beyond content and add functionalities which invite user engagement.

Both Cusdis and Disqus are not performance-friendly as they highly affect Cumulative Layout Shift (CLS).

So I set out on creating the system with Firebase, TailwindCSS and React. While TailwindCSS is not a compulsion, it's my go to library. Let's get started.

Setting Up Firebase

  • Install Firebase (Client Side) with the following command:
npm i firebase
  • Create firebase.js with the following configuration:
// File: @/lib/firebase.jsimport 'firebase/firestore'import firebase from 'firebase/app'// More about firebase config on https://firebase.google.com/docs/web/setup#config-objectvar firebaseConfig = {  apiKey: '',  authDomain: '',  projectId: '',  storageBucket: '',  messagingSenderId: '',  appId: '',}if (!firebase.apps.length) {  firebase.initializeApp(firebaseConfig)} else {  firebase.app()}export const firestore = firebase.firestore()export default firebase

Creating Like Component

  • Create the like.js file:
// File: components/blog/like.jsimport { firestore } from '@/lib/firebase'
  • Add the getLikes function which takes in the slug of the blog page, and a callback function if needed.
export const getLikes = (slug, callBackFunction) => {  firestore    .collection('likes')    .doc(slug)    .get()    .then((doc) => {      if (doc.exists) {        callBackFunction(Object.keys(doc.data()).length)      }    })    .catch((err) => {      console.error(err)    })}
  • Add the postLike function which takes in the slug of the blog page, and a callback function if needed.
export const postLike = (slug, callBackFunction) => {  fetch('https://api.ipify.org/?format=json', {    method: 'GET',  })    .then((res) => res.json())    .then((res) => {      firestore        .collection('likes')        .doc(slug)        .set(          {            [res['ip']]: null,          },          { merge: true }        )        .then(callBackFunction)    })    .catch((err) => {      console.error(err)    })}

Creating Comment Component

  • Create the comment.js file:
// File: components/blog/comments.jsimport { useState } from 'react'import firebase, { firestore } from '@/lib/firebase'
  • Adding the getComments function which takes in the slug of the blog page, and a callback function if needed.
export const getComments = (slug, callBackFunction) => {  firestore    .collection('comments')    .get()    .then((snapshot) => {      const posts = snapshot.docs        .map((doc) => doc.data())        .filter((doc) => doc.slug === slug)        .map((doc) => {          return { id: doc.id, ...doc }        })      callBackFunction(posts)    })    .catch((err) => {      console.log(err)    })}
  • Adding the writeComment function which takes in the slug of the blog page, and a callback function if needed.
export const writeComment = (name, slug, content, email, callBackFunction) => {  let temp = {    name,    slug,    content,    time: firebase.firestore.Timestamp.fromDate(new Date()),  }  if (email.length > 0) temp['email'] = email  firestore    .collection('comments')    .add(temp)    .then(() => {      callBackFunction()    })    .catch((err) => {      console.error(err)    })}
  • Creating the LoadComments function which takes in the set of the comments to display
export const LoadComments = ({ comments }) => {  return comments    .sort((a, b) =>      a.time.toDate().getTime() > b.time.toDate().getTime() ? -1 : 1    )    .map((item) => (      <div        key={item.time.seconds}        className="border dark:border-gray-500 rounded p-5 w-full mt-5 flex flex-col"      >        <span className="text-lg text-gray-500 dark:text-gray-300 font-medium">          {item.name} &middot; {item.time.toDate().toDateString()}        </span>        <span className="mt-3 text-md text-gray-500 dark:text-gray-300">          {item.content}        </span>      </div>    ))}
  • Creating the WriteComment component which takes in the slug of the blog page, and setComments for setting the new set of comments to be displayed.
const WriteComment = ({ slug, setComments }) => {  const [name, setName] = useState('')  const [email, setEmail] = useState('')  const [comment, setComment] = useState('')  return (    <form      onSubmit={(e) => {        e.preventDefault()        writeComment(name, slug, comment, email, () =>          getComments(slug, setComments)        )        setName('')        setEmail('')        setComment('')      }}      className="mt-10 flex flex-col w-full"    >      <h1 className="font-semibold text-lg">Write a comment</h1>      <div className="flex flex-col sm:flex-row sm:space-x-5 items-start">        <input          required          value={name}          placeholder="Name*"          onChange={(e) => setName(e.target.value)}          className="mt-5 w-full sm:w-1/2 appearance-none outline-none ring-0 px-5 py-2 border dark:hover:border-white hover:border-black rounded hover:shadow text-black dark:bg-black dark:text-gray-300 dark:border-gray-500"        />        <div className="mt-5 w-full sm:w-1/2 flex flex-col space-y-1">          <input            value={email}            placeholder="Email (Optional)"            onChange={(e) => setEmail(e.target.value)}            className="w-full appearance-none outline-none ring-0 px-5 py-2 border dark:hover:border-white hover:border-black rounded hover:shadow text-black dark:bg-black dark:text-gray-300 dark:border-gray-500"          />          <span className="text-sm text-gray-400">            Email will remain confidential.          </span>        </div>      </div>      <textarea        required        value={comment}        onChange={(e) => setComment(e.target.value)}        placeholder={'Comment*
Maximum of 500 characters.'} className="mt-5 appearance-none outline-none ring-0 pt-5 px-5 pb-10 border dark:hover:border-white hover:border-black rounded hover:shadow text-black dark:bg-black dark:text-gray-300 dark:border-gray-500" /> <button type="submit" className="w-[200px] appearance-none mt-5 py-2 px-5 text-center rounded border hover:bg-gray-100 dark:hover:bg-[#28282B] dark:border-gray-500" > Post a comment </button> </form> )}export default WriteComment

Creating Dynamic Blog Component

  • Load the components in the dynamic blog [slug].js file:
import WriteComment, {  getComments,  LoadComments,} from '@/components/blog/comments'export default function Post({ post }) {  const [comments, setComments] = useState([])  return <>    <WriteComment setComments={setComments} slug={post.slug} />    <div className="mt-10 pt-10 w-full border-t dark:border-gray-500">      <button        onClick={() => getComments(post.slug, setComments)}        className="w-[200px] appearance-none py-2 px-5 text-center rounded border hover:bg-gray-100 dark:hover:bg-[#28282B]   dark:border-gray-500"      >        Load Comments      </button>    </div>    <LoadComments comments={comments} />  </>}

Example

You can see an example on my blog page! The source code is available here.


Original Link: https://dev.to/digitalplayer1125/how-to-build-your-own-likes-and-comments-system-with-firebase-and-react-30o3

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To