Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React - Creating a recursive comment system that moves the reply

Let me preface this question with my experience level. I have been trying to get into web development as a hobby, and as a result, I found my way to learning React. It's been a fun experience trying to learn through trial and error. Usually, I have been following tutorials, but today, I have tried to do something a little more outside of my comfort zone.

I wish to create a comment system. All comments will be to the left side of the screen, displayed in an arbitrary manner. The user would select a comment from the left side, from where it will be displayed in full on the right side of the screen. The comment in the right side view will have a list of buttons. Each of those buttons opens up a reply to the parent comment. The child will be placed beside the parent.

Right now, I am only concerned with the logic of moving comments from the left view to the right view. In its current state, all the comments are shown in a single view, where clicking a reply button of a comment removes the reply from the main view, and places it directly beside the parent.

Given the limits of my understanding, I have worked out a half-solution. The major issue here is this bug: If a comment with no parent has two or more replies, opening all of them will cause the main list to not remove the replies from the main list, where they should be, and moved to the side of the parent. I tracked down what I believe to be the issue to the makeHidden function, or more specifically, the use of the backendComments useState. I guess that I am using it in a fundamentally wrong manner.

I apologize in advanced if I am not describing this question clearly. If my code is of no help, I would ask for a explanation on how you might tackle this problem, and please, any resources would be greatly appreciated.

CommentView.jsx

import {getComments} from '../mockDB/api'
import Comment from '../components/Comment'
import React from 'react'
import {useState, useEffect} from 'react'

function Threadview() {

    const [backendComments, setBackendComments] = useState([])
    const [cachedComments, setCachedComments] = useState([])

    var hiddenComments = [];

    const getReplies = (commentID) => {
        return cachedComments.filter(backendComment => backendComment.parentID === 
        commentID)
    }

    const makeHidden = (comment) => {

        hiddenComments.push(comment)

        var hiddenListIDs = [];
        hiddenComments.forEach((e) => hiddenListIDs.push(e.id))

        var filter = cachedComments.filter((backendComment) => 
        !hiddenListIDs.includes(backendComment.id))

        setBackendComments(filter)
    }

    useEffect(() => {
        getComments().then(data => {
            setBackendComments(data)
            setCachedComments(data)
        })
    }, [])

  return (
    <>
        {backendComments.length > 0 ?  backendComments.map((comment) =>
        <Comment key={comment.id} text={comment.text} replies={getReplies(comment.id)} 
        getReplies={getReplies} makeHidden={makeHidden}/>) : null}
    </>

}

Comment.jsx

import React from 'react'
import {useState, useEffect} from 'react'

function Comment({text, replies=[], getReplies, makeHidden}) {

    const [selectedReplies, setSelectedReplies] = useState([])

    const openReply= (comment) => {
        var newComment = <Comment key={comment.id} text={comment.text} replies= 
        {getReplies(comment.id)} getReplies={getReplies} makeHidden={makeHidden}/>

        var newList = [...selectedReplies, newComment]

        setSelectedReplies(newList)
        makeHidden(comment)

    return (
        <>
            {selectedReplies.length > 0 ? (
                <div className={styles['reply-container']}>
                    <div className={styles['reply']}>
                        {selectedReplies}
                    </div>
                </div>
            ) : (null)}

            {replies.length > 0 ? replies.map((comment) => <ReplyButton onClick={() => 
            openReply(comment)} key={comment.id} />) : null}
        </>
}

Update

I found out that the issue starts only with comments that are not nested. Comment.jsx returns the comment that was selected from a reply button to CommentView.jsx, into hiddenComments. The idea is that hiddenComments acquires and holds all of the comments that are being nested, indicating that they have to be removed from the main view. But, whenever a comment that is not nested has it's replies selected, hiddenComments get reset.

like image 740
Clause Avatar asked Feb 04 '26 18:02

Clause


1 Answers

Update

I finally solved my issue. I struggled a lot with this seemingly simple problem, and it allowed me to gain a better understanding on how React works.

As I said before, whenever a comment with no parent was being selected, it would not filter the selected replies from the main list. I thought to create another useState of hidden replies, instead of hiddenComments, and feed the list of comments to-be-hid in that. This resulted in the opposite problem. Now, only the replies opened from the comment with no parent were being filtered. It was almost like the list of comments to be hid was being reset every time a nested child was being interacted with.

I then looked at the way in which nested children were being created, which made me almost facepalm due to my stupidity. I was creating a new instance of a comment component (newComment) every time a new child needed to be created, and I was adding that comment component to the list of children to be displayed. I should have never made a new component outside of the return function, because it was somehow resetting the state in CommentView whenever it was being interacted with.

So, I simply made it so that the comment object itself, not the comment component, was being added to selectedReplies. Then, I simply mapped the comment objects in selectedReplies to comment components in the return, exactly how CommentView.jsx is set up.

Again, apologies if I am not explaining things clearly, but my changes did result in the behaviors I want for my little test, which I would count as a success. Although, I am noticing just how flawed my code is, and I will now work on refactoring all of it, to learn as much as I can. I will spare you from that digression, because I do not want you to suffer through another novel.

like image 125
Clause Avatar answered Feb 07 '26 11:02

Clause



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!