myTest532 myTest532
myTest532 myTest532

Reputation: 2381

Create a Comment Widget in React

I'm trying to create a comment widget in React. I have the data below:

const data = [
    {
        id: 1,
        comment: "Test Comment"
    },
    {
        id: 2,
        comment: "Second Comment",
        subComments: [
            {
                comment: "Sub second comment 1"
            },
            {
                comment: "Sub second comment 2"
            },
            {
                comment: "Sub second comment 3"
            }
        ]
    },
    {
        id: 3,
        comment: "How much is 2 + 2?",
        subComments: [
            {
                comment: "It is 5",
                subComments: [
                    {
                        comment: "No, it's 4."
                    }
                ],
            },
            {
                comment: "4!",
            },
            {
                comment: "Is this a joke?"
            }
        ]
    }
]

Then, I create the React app below. I have access to the data array in the component.

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

const Comment = () => {

    const [ comments, setComments ] = useState([])

    useEffect(() => {
        setComments(data);
    }, []);

    const showSubComments = (subComments) => {
        if(subComments.length === 0)
            return;
        
        subComments.map((obj, index) => {
            return (
                <>
                    <br />
                    <span style={{marginLeft: '2rem'}}>{obj.comment}</span>
                    {showSubComments(obj.subComments || [])}
                </>
            )
        });
    }

    return (
        <>
            <div>
                <textarea type="textarea" rows={5} cols={50} />
                <div>
                    {comments.map((obj, index) => {
                        return (
                            <p key={index}>
                                {obj.comment}
                                {showSubComments(obj.subComments || [])}
                            </p>
                        )
                    })}
                </div>    
            </div>   
        </>
    );
}

export default Comment;

It's not listing the subComments, just the comments. Why? The showSubComments() method should be returning the data recursively.

enter image description here

Changing the code to have a Map to easily access the subcomments in order to edit or delete them. As you can see in the image below, now the third comment 4! is displaying in the correct level with the left-margin. Does anyone know why it is happening?

enter image description here

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

const data = [
    {
        id: 1,
        comment: "Test Comment"
    },
    {
        id: 2,
        comment: "Second Comment",
        subComments: [
            {
                comment: "Sub second comment 1"
            },
            {
                comment: "Sub second comment 2"
            },
            {
                comment: "Sub second comment 3"
            }
        ]
    },
    {
        id: 3,
        comment: "How much is 2 + 2?",
        subComments: [
            {
                comment: "It is 5",
                subComments: [
                    {
                        comment: "No, it's 4."
                    }
                ],
            },
            {
                comment: "4!",
            },
            {
                comment: "Is this a joke?"
            }
        ]
    }
]

const Comment = () => {

    const [ comments, setComments ] = useState([]);
    const [ subComments, setSubComments ] = useState(new Map());
    const [ commentText, setCommentText ] = useState("");

    useEffect(() => {
        const commentsArr = [];
        const subCommentsMap = new Map();

        const setSubCommentsMap = (key, arr) => {
            if(arr === undefined)   return;
            subCommentsMap.set(key, [...arr]);
            for(let i=0; i<arr.length; i++) {
                const newKey = `${key},${i}`;
                setSubCommentsMap(newKey, arr[i].subComments)
            }
        }

        for(let i=0; i<data.length; i++) {
            commentsArr.push(data[i].comment);
            let subComments = data[i].subComments;
            let key = i.toString();
            setSubCommentsMap(key, subComments)
        }

        setComments(commentsArr);
        setSubComments(subCommentsMap);
    }, []);
    
    const displayComments = useMemo(() => {

        const displaySubComments = (subCommentsArr, margin) => {
            return subCommentsArr.map((obj, index) => {
                return (
                    <span key={index} style={{marginLeft: `${margin}rem`, display: 'block'}}>
                        {obj.comment}
                        {displaySubComments(subComments.get(`${index.toString()},${index}`) || [], margin+2)}
                    </span>
                )
            })
        }

        return comments.map((ele, index) => {
            return (
                <div key={index}>
                    <span>
                        {ele}
                        {displaySubComments(subComments.get(index.toString()) || [], 2)}
                    </span>
                </div>
            )
        })
    }, [comments, subComments])

    const handleTextOnChange = (event) => {
        setCommentText(event.target.value);
    }

    return (
        <div>
            <div>
                <textarea type="textarea" value={commentText} onChange={handleTextOnChange} rows={5} cols={50} />
                <button type="button">Add Comment</button>
            </div>
            <div>
                {displayComments}
            </div>
        </div>
    )
}

export default Comment;

Upvotes: 0

Views: 347

Answers (2)

Amila Senadheera
Amila Senadheera

Reputation: 13245

You have forgotten to return after the map function

Try below:

    const showSubComments = (subComments) => {
        if (subComments.length === 0) return;

        return subComments.map((obj, index) => {
            return (
                <div>
                    <br />
                    <div style={{ marginLeft: "2rem" }}>{obj.comment}</div>
                    <div style={{ marginLeft: "4rem" }}>
                        {showSubComments(obj.subComments || [])}
                    </div>
                </div>
            );
        });
    };

Just call the showSubComments with comments param

        return (
            <>
                <div>
                    <textarea type="textarea" rows={5} cols={50} />
                    <div>{showSubComments(comments)}</div>
                </div>
            </>
        );

enter image description here

Upvotes: 1

Isaac
Isaac

Reputation: 12874

The problem is just about missing a single keyword return. You have tranformed subComments to react component, but you did not return to render them

return subComments.map((obj, index) => {
            return (
                <>
                    <br />
                    <span style={{marginLeft: '2rem'}}>{obj.comment}</span>
                    {showSubComments(obj.subComments || [])}
                </>
            )
        });

Upvotes: 1

Related Questions