Fixing Duplicate Error & Adding Comments in Our CloudNoteBook App ๐๐
While testing our CloudNoteBook app, you may have noticed a tricky bug โ clicking
Add Note quickly (or submitting the same note twice) creates duplicate notes
with the same key, which React doesn't like at all. In this tutorial, we'll squash
that bug for good, and then reward ourselves by adding a brand-new Comments feature so
users can leave notes-on-notes right inside CloudNoteBook.
In this React JS tutorial for beginners, we will learn:
key warnings happen in Reactuuidcomments array to each note's data structureaddComment method to update nested state safelyWhy Do Duplicate Notes Happen?
This bug usually shows up in one of two ways: either the user double-clicks the
Add Note button before the state updates, or notes are being keyed using
index instead of a stable, unique id. When two notes end up with
the same key, React can't tell them apart while re-rendering, which leads
to duplicate entries, mismatched updates, and console warnings like
"Warning: Encountered two children with the same key."
Step 1: Give Every Note a Truly Unique ID
Let's install the uuid package so every note gets a guaranteed-unique identifier,
instead of relying on array index or timestamps that can collide.
npm install uuid
import { v4 as uuidv4 } from "uuid";
Step 2: Add a Guard Check Before Adding a Note
Open Notes.js and update the addNote method so it checks whether an
identical, not-yet-saved note is already being submitted, and ignores the click if so.
This stops accidental double-submits from creating duplicates.
addNote = (title, description, tag) => {
// Guard: ignore duplicate rapid submissions of the same note
const isDuplicate = this.state.notes.some(
(note) =>
note.title.trim().toLowerCase() === title.trim().toLowerCase() &&
note.description.trim().toLowerCase() === description.trim().toLowerCase()
);
if (isDuplicate) {
console.warn("Duplicate note detected โ submission ignored.");
return;
}
const newNote = {
id: uuidv4(),
title,
description,
tag,
comments: [],
};
this.setState({ notes: [...this.state.notes, newNote] });
};
Step 3: Use note.id as the key, Not the Index
Wherever we render the list of notes, make sure the key prop uses
note.id instead of the array index. This alone fixes most "duplicate key" console
warnings.
{notes.map((note) => (
<Noteitem key={note.id} note={note} />
))}
Step 4: Add a comments Array to Every Note
To support comments, every note object now carries its own comments array
(as shown in addNote above). Each comment will be a small object with its own
unique id and text.
// Example note shape after this update
{
id: "a1b2c3",
title: "Grocery List",
description: "Milk, eggs, bread",
tag: "Personal",
comments: [
{ id: "c1", text: "Don't forget oat milk!" }
]
}
Step 5: Write the addComment Method
This method finds the correct note by id and appends a new comment to its
comments array โ without mutating state directly.
addComment = (noteId, commentText) => {
if (!commentText.trim()) return;
const updatedNotes = this.state.notes.map((note) => {
if (note.id === noteId) {
return {
...note,
comments: [...note.comments, { id: uuidv4(), text: commentText }],
};
}
return note;
});
this.setState({ notes: updatedNotes });
};
Step 6: Render Comments Inside Noteitem.js
Now let's add a small comment box and comment list inside each note card. This uses local component state to hold the text being typed before it's submitted.
import React, { useState, useContext } from "react";
import NoteContext from "../../context/notes/NoteContext";
const Noteitem = (props) => {
const context = useContext(NoteContext);
const { addComment } = context;
const { note } = props;
const [commentText, setCommentText] = useState("");
const handleAddComment = () => {
addComment(note.id, commentText);
setCommentText("");
};
return (
<div className="col-md-4 my-3">
<div className="card">
<div className="card-body">
<h5 className="card-title">{note.title}</h5>
<p className="card-text">{note.description}</p>
{/* Comments Section */}
<div className="mt-3">
<h6>Comments ({note.comments.length})</h6>
<ul className="list-group mb-2">
{note.comments.map((comment) => (
<li key={comment.id} className="list-group-item py-1">
{comment.text}
</li>
))}
</ul>
<div className="input-group">
<input
type="text"
className="form-control"
placeholder="Add a comment..."
value={commentText}
onChange={(e) => setCommentText(e.target.value)}
/>
<button className="btn btn-outline-warning" onClick={handleAddComment}>
Post
</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default Noteitem;
Bug Cause vs Fix โ Quick Reference
| Problem | Root Cause | Fix Applied |
|---|---|---|
| Duplicate notes on fast clicks | No check for identical, recently-submitted note data | Guard check inside addNote before adding to state |
| "Two children with same key" warning | Using array index as the React key |
Switched to a stable note.id generated with uuid |
| Comments not persisting per note | No comments field on the note object |
Added comments: [] to every note and an addComment updater |
Features and Learnings:-
uuid package to generate stable, collision-free note IDs.addNote to block accidental duplicate submissions.note.id across the notes list.comments array.addComment updater using map() and spread syntax.Noteitem card.