分析
用户想要一个能够显示评论列表,并且允许用户进行回复的组件。可能还需要支持多级回复,也就是对回复进行再回复。然后,我要考虑组件的结构和功能。
首先,数据结构方面,评论应该包含id、内容、作者、时间,以及可能的子回复。子回复的结构可能和评论类似,所以可以设计一个通用的结构,比如每个评论有一个replies数组,里面存放回复对象。
接下来是组件的状态管理。评论列表应该用useState来管理,这样当添加新评论或回复时,组件可以重新渲染。还需要处理用户输入,所以需要状态来保存输入的文本。
然后是UI部分。评论和回复可能需要递归渲染,因为每个回复下面可能还有更多的回复。所以可以考虑用一个递归组件来渲染评论及其子回复。每个评论项需要显示作者、内容、时间,还有一个回复按钮。点击回复按钮时,要显示一个输入框,让用户输入回复内容。
输入框部分,可能需要根据当前回复的父评论ID来判断在哪里显示输入框。例如,当点击某个评论的回复按钮时,输入框应该出现在该评论的下方。因此,需要有一个状态来跟踪当前正在回复的评论ID。
提交回复的函数需要处理两种情况:一种是直接评论,另一种是回复某个具体的评论。提交时,需要更新评论列表,添加新的回复到对应的replies数组中。这里可能需要一个函数来递归查找父评论并添加回复,或者使用辅助函数来处理嵌套结构。
样式方面,用户可能希望有不同的缩进来表示层级关系。比如,顶级评论没有缩进,一级回复缩进一定距离,二级回复再缩进更多,以此类推。可以用paddingLeft或者marginLeft来实现,根据层级动态调整。
还需要考虑组件的可定制性,比如允许用户传入自定义样式、评论和回复的渲染方法,或者回调函数当提交评论时。这样组件更灵活,用户可以根据需要调整外观和行为。
测试一下功能,比如添加评论、回复评论,确保状态更新正确,UI渲染正确。特别是多级回复的情况,要确保递归渲染正确,并且输入框出现在正确的位置。
能的优化点:使用useCallback来避免函数重复创建,提高性能。特别是提交回复的函数和回复按钮的回调函数。另外,对于深层嵌套的评论,递归渲染可能会导致性能问题,但一般情况下的评论层级不会太深,所以应该可以接受。
最后,提供示例用法,让用户知道如何传入初始数据,以及如何使用组件。可能需要提醒用户根据需要调整样式,或者处理时间格式化等问题,比如使用date-fns或其他库来格式化时间显示。
实现代码
封装一个可复用的评论回复组件,支持多级嵌套回复。以下是代码实现:
import React, { useState } from 'react';
import PropTypes from 'prop-types';
// 评论项组件
const CommentItem = ({
comment,
depth = 0,
onReply,
activeReplyId,
setActiveReplyId,
parentAuthor
}) => {
const maxDepth = 4; // 控制最大嵌套深度
const canReply = depth < maxDepth;
return (
<div
className="comment-item"
style={{ marginLeft: depth * 32 }}
>
<div className="comment-header">
<span className="comment-author">{comment.author}</span>
{parentAuthor && (
<span className="reply-to">回复 @{parentAuthor}</span>
)}
<span className="comment-time">{comment.time}</span>
</div>
<div className="comment-content">{comment.content}</div>
{canReply && (
<button
className="reply-btn"
onClick={() => setActiveReplyId(comment.id)}
>
回复
</button>
)}
{/* 回复输入框 */}
{activeReplyId === comment.id && (
<CommentInput
onSubmit={(content) => onReply(comment.id, content)}
onCancel={() => setActiveReplyId(null)}
/>
)}
{/* 递归渲染子回复 */}
{comment.replies?.map((reply) => (
<CommentItem
key={reply.id}
comment={reply}
depth={depth + 1}
onReply={onReply}
activeReplyId={activeReplyId}
setActiveReplyId={setActiveReplyId}
parentAuthor={comment.author}
/>
))}
</div>
);
};
// 评论输入框组件
const CommentInput = ({ onSubmit, onCancel }) => {
const [content, setContent] = useState('');
const handleSubmit = () => {
if (content.trim()) {
onSubmit(content);
setContent('');
}
};
return (
<div className="comment-input-container">
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
placeholder="输入评论内容..."
rows={3}
/>
<div className="input-buttons">
<button onClick={handleSubmit}>提交</button>
<button onClick={onCancel}>取消</button>
</div>
</div>
);
};
// 主组件
const CommentComponent = ({
initialComments,
currentUser = '匿名用户'
}) => {
const [comments, setComments] = useState(initialComments);
const [activeReplyId, setActiveReplyId] = useState(null);
// 添加评论/回复
const addComment = (content, parentId = null) => {
const newComment = {
id: Date.now(),
author: currentUser,
content,
time: new Date().toLocaleString(),
replies: []
};
if (!parentId) {
setComments([...comments, newComment]);
} else {
const updateComments = (comments) =>
comments.map(comment => {
if (comment.id === parentId) {
return {
...comment,
replies: [...comment.replies, newComment]
};
}
return {
...comment,
replies: updateComments(comment.replies)
};
});
setComments(prev => updateComments(prev));
}
};
return (
<div className="comment-container">
<CommentInput onSubmit={(content) => addComment(content)} />
<div className="comments-list">
{comments.map((comment) => (
<CommentItem
key={comment.id}
comment={comment}
onReply={addComment}
activeReplyId={activeReplyId}
setActiveReplyId={setActiveReplyId}
/>
))}
</div>
</div>
);
};
// Prop 类型校验
CommentComponent.propTypes = {
initialComments: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
author: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
time: PropTypes.string,
replies: PropTypes.array
})
),
currentUser: PropTypes.string
};
export default CommentComponent;
组件特性说明:
- 核心功能:
- 支持多级嵌套回复(默认最多4层)
- 显示回复对象(回复 @xxx)
- 支持自定义当前用户
- 支持初始评论数据传入
- 自动生成评论时间
- 组件结构:
CommentComponent
:主组件CommentItem
:单个评论项(支持递归渲染)CommentInput
:评论输入组件
- 使用示例:
// 初始化数据示例
const initialComments = [
{
id: 1,
author: '用户A',
content: '第一条评论',
time: '2023-08-01 10:00',
replies: [
{
id: 2,
author: '用户B',
content: '第一条回复',
time: '2023-08-01 10:05'
}
]
}
];
// 使用组件
<CommentComponent
initialComments={initialComments}
currentUser="当前用户"
/>
- 样式建议:
.comment-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.comment-item {
margin: 16px 0;
padding: 12px;
border: 1px solid #eee;
border-radius: 4px;
}
.comment-header {
display: flex;
align-items: center;
margin-bottom: 8px;
color: #666;
}
.comment-author {
font-weight: bold;
margin-right: 8px;
}
.reply-to {
color: #999;
margin-right: 8px;
}
.comment-time {
font-size: 0.9em;
}
.reply-btn {
background: none;
border: none;
color: #1890ff;
cursor: pointer;
padding: 4px 8px;
}
.comment-input-container {
margin: 16px 0;
}
.comment-input-container textarea {
width: 100%;
padding: 8px;
margin-bottom: 8px;
}
.input-buttons button {
margin-right: 8px;
padding: 4px 12px;
}
- 扩展建议:
- 增加点赞功能
- 支持富文本输入
- 添加评论删除功能
- 支持用户头像显示
- 增加评论加载更多功能
- 支持@用户功能
使用时请根据实际需求调整样式和功能实现,可以通过props传递更多配置项(如最大嵌套深度、日期格式等)来增强组件的灵活性。