Skip to content

WebNinjaDeveloper.com

Programming Tutorials




Menu
  • Home
  • Youtube Channel
  • Official Blog
  • Nearby Places Finder
  • Direction Route Finder
  • Distance & Time Calculator
Menu

Build a Next.js Firestore Blog Post CRUD App Using react-quill WYSIWYG Editor in Browser

Posted on February 12, 2023

 

 

Welcome folks today in this blog post we will be building a next.js firestore blog post crud app using react-quill wysiwyg editor in browser. All the full source code of the application is shown below.

 

 

Get Started

 

 

In order to get started you need to create the next.js app using the below command as shown below

 

 

npx create-next-app@latest

 

 

Now you need to install the below libraries using the below command as shown below

 

 

npm i firebase

 

 

npm i react-quill

 

 

And now you will see the final directory structure of the next.js app is shown below

 

 

 

 

Now you need to create the firebaseConfig.js file for storing the firestore details about the project as shown below

 

 

firebaseConfig.js

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
import { initializeApp } from "firebase/app";
import { getFirestore } from 'firebase/firestore'
const firebaseConfig = {
    apiKey: "",
    authDomain: "",
    projectId: "",
    storageBucket: "",
    messagingSenderId: "",
    appId: ""
};
 
export const app = initializeApp(firebaseConfig);
export const database = getFirestore(app);

 

 

 

Now you need to replace your own firebase project data inside the above code

 

 

Now we need to edit the index.js file and copy paste the following code

 

 

index.js

 

 

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import Head from 'next/head'
import styles from '../styles/Evernote.module.scss'
import NoteOperations from './components/NoteOperations';
import NoteDetails from './components/NoteDetails';
import { useState } from 'react';
 
export default function Home() {
  const [ID, setID] = useState(null);
  const getSingleNote = (id) => {
    setID(id)
  }
  return (
    <div className={styles.container}>
      <Head>
        <title>Evernote Clone</title>
        <meta name="description" content="This is an Evernote Clone" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
 
      <main>
        <div className={styles.container}>
          <div className={styles.left}>
            <NoteOperations getSingleNote={getSingleNote} />
          </div>
          <div className={styles.right}>
            <NoteDetails ID={ID} />
          </div>
        </div>
      </main>
    </div>
  )
}

 

 

As you can see we are including two components inside our blog app first one is the component where we will be showing the list of blog posts and the next component will be showing the details of the note. And also we are importing the useState hook to declare the state of the application.

 

 

Now you need to create the components folder inside the root directory and create the NoteOperations.js file and copy paste the following code

 

 

components/NoteOperations.js

 

 

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import styles from '../../styles/Evernote.module.scss'
import { useState, useEffect } from 'react';
import { app, database } from '../../firebaseConfig';
import { collection, addDoc, getDocs } from 'firebase/firestore';
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';
 
const dbInstance = collection(database, 'notes');
 
export default function NoteOperations({ getSingleNote }) {
    const [isInputVisible, setInputVisible] = useState(false);
    const [noteTitle, setNoteTitle] = useState('');
    const [noteDesc, setNoteDesc] = useState('');
    const [notesArray, setNotesArray] = useState([]);
    const inputToggle = () => {
        setInputVisible(!isInputVisible)
    }
 
    const addDesc = (value) => {
        setNoteDesc(value)
    }
 
    const saveNote = () => {
        addDoc(dbInstance, {
            noteTitle: noteTitle,
            noteDesc: noteDesc
        })
            .then(() => {
                setNoteTitle('')
                setNoteDesc('')
                getNotes();
            })
    }
 
    const getNotes = () => {
        getDocs(dbInstance)
            .then((data) => {
                setNotesArray(data.docs.map((item) => {
                    return { ...item.data(), id: item.id }
                }));
            })
    }
 
    useEffect(() => {
        getNotes();
    }, [])
    return (
        <>
            <div className={styles.btnContainer}>
                <button
                    onClick={inputToggle}
                    className={styles.button}>
                    Add a New Note
                </button>
            </div>
 
            {isInputVisible ? (
                <div className={styles.inputContainer}>
                    <input
                        className={styles.input}
                        placeholder='Enter the Title..'
                        onChange={(e) => setNoteTitle(e.target.value)}
                        value={noteTitle}
                    />
                    <div className={styles.ReactQuill}>
                        <ReactQuill
                            onChange={addDesc}
                            value={noteDesc}
                        />
                    </div>
                    <button
                        onClick={saveNote}
                        className={styles.saveBtn}>
                        Save Note
                    </button>
                </div>
            ) : (
                <></>
            )}
 
            <div className={styles.notesDisplay}>
                {notesArray.map((note) => {
                    return (
                        <div
                            className={styles.notesInner}
                            onClick={() => getSingleNote(note.id)}>
                            <h4>{note.noteTitle}</h4>
                        </div>
                    )
                })}
            </div>
        </>
    )
}

 

 

As you can see in the above code we are displaying all the notes from the firestore database and then we are performing the crud operations. And here we are using the map() method to loop through all the notes inside the array and display it inside the browser. And for each note we have assigned the onclick listener to each note to get the details of the single note which is selected.

 

 

 

 

And now when you click the add note button you will see the simple WYSIWYG editor using the react-quill library. And here we will be constructing the blog post using advanced formatting options as shown below

 

 

 

 

Now we need to create the NoteDetails.js component where we will be showing the different details of the blog post where we will be also deleting the blog post if you press the delete button

 

 

NoteDetails.js

 

 

JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import styles from '../../styles/Evernote.module.scss'
import { useState, useEffect } from 'react'
import { app, database } from '../../firebaseConfig';
import {
    doc,
    getDoc,
    getDocs,
    collection,
    updateDoc,
    deleteDoc
} from 'firebase/firestore'
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';
const dbInstance = collection(database, 'notes');
 
export default function NoteDetails({ ID }) {
    const [singleNote, setSingleNote] = useState({})
    const [isEdit, setIsEdit] = useState(false);
    const [noteTitle, setNoteTitle] = useState('');
    const [noteDesc, setNoteDesc] = useState('');
    const getSingleNote = async () => {
        if (ID) {
            const singleNote = doc(database, 'notes', ID)
            const data = await getDoc(singleNote)
            setSingleNote({ ...data.data(), id: data.id })
        }
    }
 
    const getNotes = () => {
        getDocs(dbInstance)
            .then((data) => {
                setSingleNote(data.docs.map((item) => {
                    return { ...item.data(), id: item.id }
                })[0]);
            })
    }
 
    const getEditData = () => {
        setIsEdit(true);
        setNoteTitle(singleNote.noteTitle);
        setNoteDesc(singleNote.noteDesc)
    }
 
    useEffect(() => {
        getNotes();
    }, [])
 
    useEffect(() => {
        getSingleNote();
    }, [ID])
 
    const editNote = (id) => {
        const collectionById = doc(database, 'notes', id)
 
        updateDoc(collectionById, {
            noteTitle: noteTitle,
            noteDesc: noteDesc,
        })
            .then(() => {
                window.location.reload()
            })
    }
 
    const deleteNote = (id) => {
        const collectionById = doc(database, 'notes', id)
 
        deleteDoc(collectionById)
            .then(() => {
                window.location.reload()
            })
    }
    return (
        <>
            <div>
                <button
                    className={styles.editBtn}
                    onClick={getEditData}
                >Edit
                </button>
                <button
                    className={styles.deleteBtn}
                    onClick={() => deleteNote(singleNote.id)}
                >Delete
                </button>
            </div>
            {isEdit ? (
                <div className={styles.inputContainer}>
                    <input
                        className={styles.input}
                        placeholder='Enter the Title..'
                        onChange={(e) => setNoteTitle(e.target.value)}
                        value={noteTitle}
                    />
                    <div className={styles.ReactQuill}>
                        <ReactQuill
                            onChange={setNoteDesc}
                            value={noteDesc}
                        />
                    </div>
                    <button
                        onClick={() => editNote(singleNote.id)}
                        className={styles.saveBtn}>
                        Update Note
                    </button>
                </div>
            ) : (
                <></>
            )}
            <h2>{singleNote.noteTitle}</h2>
            <div dangerouslySetInnerHTML={{ __html: singleNote.noteDesc }}></div>
        </>
    )
}

 

 

As you can see in the above code we are able to edit the note or blog post when we click it. And we are doing this editing in an editor. And also we are deleting the blog post using the deleteDoc() method and inside this method we are passing the id of the blog post that we want to delete it.

 

 

 

 

GITHUB REPOSITORY

 

Recent Posts

  • Angular 14/15 JWT Login & Registration Auth System in Node.js & Express Using MongoDB in Browser
  • Build a JWT Login & Registration Auth System in Node.js & Express Using MongoDB in Browser
  • React-Admin Example to Create CRUD REST API Using JSON-Server Library in Browser Using Javascript
  • Javascript Papaparse Example to Parse CSV Files and Export to JSON File and Download it as Attachment
  • Javascript Select2.js Example to Display Single & Multi-Select Dropdown & Fetch Remote Data Using Ajax in Dropdown
  • Angular
  • Bunjs
  • C#
  • Deno
  • django
  • Electronjs
  • java
  • javascript
  • Koajs
  • kotlin
  • Laravel
  • meteorjs
  • Nestjs
  • Nextjs
  • Nodejs
  • PHP
  • Python
  • React
  • ReactNative
  • Svelte
  • Tutorials
  • Vuejs




©2023 WebNinjaDeveloper.com | Design: Newspaperly WordPress Theme