Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions src/components/Comment/Comment.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
$mobile-only: "only screen and (max-width : 768px)";
$tablet-only: "only screen and (max-width : 1024px)";

a {
font-weight: bold;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}

.meta {
font-size: 13px;
color: #696969;
font-weight: bold;
letter-spacing: 0.5px;
margin-bottom: 8px;
a {
text-decoration: none;

&:hover {
text-decoration: underline;
}
}
.time {
padding-left: 5px;
}
}

@media #{$mobile-only} {
.meta {
font-size: 14px;
margin-bottom: 10px;
.time {
padding: 0;
float: right;
}
}
}

.meta-collapse {
margin-bottom: 20px;
}

.deleted-meta {
font-size: 12px;
font-weight: bold;
letter-spacing: 0.5px;
margin: 30px 0;
a {
text-decoration: none;
}
}

.collapse {
font-size: 13px;
letter-spacing: 2px;
cursor: pointer;
}

.comment-tree {
margin-left: 24px;
}

@media #{$tablet-only} {
.comment-tree {
margin-left: 8px;
}
}

.comment-text {
font-size: 15px;
margin-top: 0;
margin-bottom: 20px;
word-wrap: break-word;
line-height: 1.5em;
}

.subtree {
margin-left: 0;
padding: 0;
list-style-type: none;
}
49 changes: 49 additions & 0 deletions src/components/Comment/Comment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { CommentProps } from './Comment.types';
import styles from './Comment.module.scss';

const Comment: React.FC<CommentProps> = ({ comment }) => {
const [collapse, setCollapse] = useState(false);

if (comment.deleted) {
return (
<div>
<div className={styles['deleted-meta']}>
<span className={styles.collapse}>[deleted]</span> | Comment Deleted
</div>
</div>
);
}

return (
<div>
<div className={collapse ? `${styles.meta} ${styles['meta-collapse']}` : styles.meta}>
<span className={styles.collapse} onClick={() => setCollapse(!collapse)}>
[{collapse ? '+' : '-'}]
</span>{' '}
<Link to={`/user/${comment.user}`}>{comment.user}</Link>
<span className={styles.time}>{comment.time_ago}</span>
</div>
<div className={styles['comment-tree']}>
{!collapse && (
<div>
<p
className={styles['comment-text']}
dangerouslySetInnerHTML={{ __html: comment.content }}
/>
<ul className={styles.subtree}>
{comment.comments.map((subComment) => (
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 comment.comments.map() crashes if comments is undefined/null from API

Calling .map() directly on comment.comments at line 36 will throw a TypeError at runtime if the API returns a comment object where comments is undefined or null.

Root Cause

The data comes from an external API (node-hnapi.herokuapp.com), and the TypeScript interface at src/components/Comment/Comment.types.ts:9 declares comments: Comment[] as always present, but this provides no runtime guarantee. If the API ever returns a comment without a comments field (or with null), the call to comment.comments.map(...) will throw:

TypeError: Cannot read properties of undefined (reading 'map')

Notably, the original Angular implementation at src/app/item-details/comment/comment.component.html:11 uses *ngFor="let subComment of comment.comments", which silently handles null/undefined by simply not iterating. The React port loses this safety.

Impact: A single comment with a missing comments field will crash the entire comment tree (and potentially the whole page if there's no error boundary), since React will propagate the error up.

Suggested change
{comment.comments.map((subComment) => (
{(comment.comments || []).map((subComment) => (
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

<li key={subComment.id}>
<Comment comment={subComment} />
</li>
))}
</ul>
</div>
)}
</div>
</div>
);
};

export default Comment;
14 changes: 14 additions & 0 deletions src/components/Comment/Comment.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface Comment {
id: number;
level: number;
user: string;
time: number;
time_ago: string;
content: string;
deleted: boolean;
comments: Comment[];
}

export interface CommentProps {
comment: Comment;
}