Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
YouTube Security VideosWelcome to GitHub Copilot Day: the future of agentic engineering(22.09.2026 um 20:00 Uhr)
YouTube Security VideosMicrosoft Mechanics: What Can a Copilot Agent Actually Read?(22.09.2026 um 20:27 Uhr)
Unix & Linux ServerPeppermintOS Is Moving From Xorg to XLibre to Avoid Wayland(22.09.2026 um 19:58 Uhr)
Sicherheitslücken (CVE)USN-8803-1: Sudo vulnerability(22.09.2026 um 16:15 Uhr)
Sichere ProgrammierungClaude Opus 5.5 is now available in GitHub Copilot(22.09.2026 um 19:10 Uhr)
Sichere ProgrammierungColab is now part of your Google AI plan(22.09.2026 um 20:51 Uhr)
Sichere ProgrammierungThe Hidden Production Risks of Third-Party SDKs(22.09.2026 um 20:00 Uhr)
YouTube Security VideosWelcome to GitHub Copilot Day: the future of agentic engineering(22.09.2026 um 20:00 Uhr)
YouTube Security VideosMicrosoft Mechanics: What Can a Copilot Agent Actually Read?(22.09.2026 um 20:27 Uhr)
Unix & Linux ServerPeppermintOS Is Moving From Xorg to XLibre to Avoid Wayland(22.09.2026 um 19:58 Uhr)
Sicherheitslücken (CVE)USN-8803-1: Sudo vulnerability(22.09.2026 um 16:15 Uhr)
Sichere ProgrammierungClaude Opus 5.5 is now available in GitHub Copilot(22.09.2026 um 19:10 Uhr)
Sichere ProgrammierungColab is now part of your Google AI plan(22.09.2026 um 20:51 Uhr)
Sichere ProgrammierungThe Hidden Production Risks of Third-Party SDKs(22.09.2026 um 20:00 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

Recursively rendering comments for a developer content creation site ♻️

I’ve been working on a new blogging developer content creation site (which I’ll be sharing more about soon), and I recently ran into a small problem - how on earth do you render a comments section? render(comments) // what should happen …

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

I’ve been working on a new blogging developer content creation site (which I’ll be sharing more about soon), and I recently ran into a small problem - how on earth do you render a comments section?




render(comments) // what should happen in this function?






Not just a list of comments, of course, but a fully functioning comments section specifically with the option to indent replies. It’s a little bit more complicated than you might think, and today, I’d like to share my solution with you.






A small outline ✍️



Before I do anything, it would be wise to give you a brief overview of the way I expect this comments section to work.



Each comment should:




  • Have general info about each comment (ex. username, date created, date edited)

  • Have a reply button, with the ability to reply (some sort of textbox to enter text in)

  • Have an edit/delete button on each comment, if the comment belongs to the current user



And the comments section as a whole should:





  • specifically have the option to indent comments that are replies.



Firstly, we need to add some basic features to each Comment component – namely the previously mentioned “general info”, reply button, and the edit/delete button. This isn’t that interesting though. It’s just a bunch of variables and ternary operators. Let’s get to the fun part – recursively rendering the comments.






The part you came for 🎊



Now I know what you’re thinking. Can’t you just mark each comment with as a reply/not a reply and then indent it if it is? Well, yes. But that’s not very extensible. If I ever want to do anything else with this comment section (like indenting replies to replies), I need to do something a tinyyy bit fancier. Additionally, I still need to render replies in order on the client’s end, so a basic boolean value on each comment wouldn’t really be appropriate here.



One option would be to calculate the “depth” of the comment on the server-side (in this case, using the Django ORM/Postgres) and then render it, like so:



Diagram of comments



… and while this isn’t a bad idea (recursive queries do exist!), why force our server to do work that the client could do by themselves?



And so I went looking for a client-side solution. I tried to apply a certain CSS class if the comment was a reply, and then another CSS class if said comment was a reply to a reply… but that just ended up being a whole heap of conditionals that didn’t really lead me anywhere.






A new idea 💡



If you recall a few paragraphs ago, I mentioned the idea of a recursive query on the server-side. I didn’t want to use it because again, why force our server to do work that our client could? But the idea of utilizing recursion was certainly plausible. After a bit of thinking, I remembered a technique that I learned from my time spent practicing LeetCode: backtracking. It should be noted that in the code you’ll see in just a moment, one may argue that this “backtracking” algorithm is more of a tree traversal. Either way, it was an idea.



It should be noted that backtracking is horribly inefficient for an input n larger than a few thousand… but when am I ever going to render 5000 comments all at once? Likely never. In fact, one of the biggest posts on Dev.to has only 189 comments (at the time of writing this, October 29th, 2024).



This is still viable, right? Well… yes! I’m sure there’s a better solution, but for my use case, this is totally fine. Let’s take a look at an example before we put this into code. Here’s our input:




// this is a very simplified version of the input, where the id is the id of the comment, content is the content of the comment, and reply refers to the comment id that the comment should be replying to. 

const data = [
{ id: 1, replyto: null, content: "content" },
{ id: 2, replyto: null, content: "content" },
{ id: 3, replyto: 1, content: "content" },
{ id: 4, replyto: 1, content: "content" },
{ id: 5, replyto: 2, content: "content" },
{ id: 6, replyto: 3, content: "content" },
];






Now, we need to go through this input, measure the “depth” of each comment, and indent that comment according to its depth.



Let’s start with measuring the depth of each comment (AKA creating a few trees of comments using objects). We can do this like so (diagram first, then code!):



Diagram of below code



And for the code




const input = {};
const haveSeen = new Set([]);
data.forEach((comment) => {
if (comment.replyto != null) {
input[comment.replyto] = {
...input[comment.replyto],
[comment.id]: comment.content,
};
haveSeen.add(comment.id);
haveSeen.add(comment.replyto);
}
});

data.forEach((comment) => {
if (haveSeen.has(comment.id) == false) {
input[comment.id] = {};
}
});






I won’t walk through each step of this part of the algorithm, but it’s pretty simple. The complicated part is all of the different variable names.






The rendering process 🖥️



Now that we have our “data structure”, all we need to do is traverse it and indent each comment according to its depth (AKA “rendering” it). I put the word “render” in quotes, because in this example, we aren’t actually rendering anything. We can do this recursively, as I previously mentioned, and use memoization to speed up our algorithm a little bit:




const memo = new Set([]);

function render(nodes, level) {
return nodes.map((key) => {
if (input[key] != undefined && memo.has(key) == false) {
const res = [key, render(Object.keys(input[key]), level + 1)];
memo.add(key);
return res
}
if (memo.has(key) == true) {
return null
}
return key;
});
}

render(Object.keys(input), 1)






There’s a lot going on here, so let’s break it down in pseudocode:




  • Declare a Set named “memo”.

  • Call the recursive render function.

  • If the comment at the current level has at least 1 or more replies and the comment’s ID is not in the memo, return the result of recursively calling the render function again.

  • If the comment doesn’t have any replies but the ID is in the memo, return null.

  • Finally, if the comment doesn’t have any replies but the ID is not in the memo, return the key.



You may have noticed that we’re returning nodes.map(...) inside the render function, and not just “key”. This is a tiny bit weird, but understand that this is necessary in practice. If it seems weird or confusing, just ignore it for now. Also, in practice, nodes.map(...)’s function does not return a key. It instead returns a JSX element.






Closing up 🏁



Finally, here’s how the render function is actually called:




render(Object.keys(input), 1)






And … that’s it! Before you go, you might be interested in taking a look at how we actually use this in practice. It should clear up any confusion about why we return nodes.map(...) instead of just the key. I’ll attach the full code below.



Also, if you enjoyed this article, please consider reading some other things that I’ve written!






Extra info



Here’s how the comment section actually looks in practice:



Final product



And here’s the code:




const memo = new Set([]);

function renderedCommentsHelper(nodes, level) {
const res = nodes.map((key) => {
if (input[key] != undefined && memo.has(key) == false) {
const recRes = (
<>
<Comment/>
{renderedCommentsHelper(Object.keys(input[key]), level + 1)}
</>
);
memo.add(key);
return recRes;
}
if (memo.has(key) == true) {
return null;
}
return (
<Comment/>
);
});
return res;
}

// creating an object to access any and all comment data in O(1)
const data = {};
raw.forEach((comment) => {
data[comment.id] = comment;
});

// creating a tree to run renderedCommentsHelper on
const input = {};
const haveSeen = new Set([]);
raw.forEach((comment) => {
if (comment.reply_to != null) {
input[comment.reply_to.id] = {
...input[comment.reply_to.id],
[comment.id]: null,
};
haveSeen.add(comment.id);
haveSeen.add(comment.reply_to.id);
}
});

raw.forEach((comment) => {
if (haveSeen.has(comment.id) == false) {
input[comment.id] = {};
}
});

return (
<ul className="list" key={1}>
{renderedCommentsHelper(Object.keys(input), 1)}
</ul>
);


Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Recursively rendering comments for a developer content creation site ♻️

Thematisch verwandte Begriffe: Recursively, rendering, comments, developer · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-77258 | MCP Atlassian is a Model Context Protocol (MCP) server for Atlassian pro…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick