我有一個很大的Comment
組件,效果很好,但相當長。我最近在UI中添加了一個報告按鈕,用於切換reported
狀態,該狀態應該改變評論的輸出(刪除所有內容並顯示報告的消息)。試圖在組件的返回方法中寫if語句讓我意識到我應該分解這個組件(更不用說我已經發現自己複製/粘貼了這個組件和非常類似的Reply
組件之間的很多代碼)。React - 將大型評論組件拆分爲多個組件
該評論有3個主要「視圖」 - 默認視圖,報告視圖和「我的評論」視圖。
每當我試圖拆分過去的組件時,我發現自己陷入了將多個道具傳遞給每個組件的困境。我不確定自己是否做錯了,或者是否只是我需要習慣的東西。任何提示分解這個組件的最佳方式將不勝感激。
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { replyToCommentService, deleteCommentService, reportCommentService } from '../../../services/CommentService';
import { likeService, removeLikeService } from '../../../services/LikeService';
import Reply from './Reply';
import Avatar from '../Avatars/Avatar';
import IconWithText from '../Icons/IconWithText';
import CommentForm from './CommentForm';
import Dropdown from '../Dropdowns/Dropdown';
import DropdownSection from '../Dropdowns/DropdownSection';
export default class Comment extends Component {
constructor(props) {
super(props);
this.state = {
replies: this.props.replies,
showReply: false,
reply: '',
replyBtnDisabled: true,
liked: this.props.liked,
numberOfLikes: this.props.likes.length,
moreActionsActive: false,
reported: this.props.reported,
};
}
handleInput = (reply) => {
this.setState({ reply },() => {
this.fieldComplete();
});
}
fieldComplete =() => {
if (this.state.reply.length) {
this.setState({ replyBtnDisabled: false });
} else {
this.setState({ replyBtnDisabled: true });
}
}
toggleReply =() => {
this.setState({ showReply: !this.state.showReply },() => {
if (this.state.showReply === true) {
this.replyInput.focus();
}
});
}
postReply =() => {
const data = { comment_id: this.props.id, comment_content: this.state.reply };
replyToCommentService(data, this.postReplySuccess, this.error);
}
postReplySuccess = (res) => {
this.setState({ replies: this.state.replies.concat(res.data) });
this.toggleReply();
this.handleInput('');
}
error = (res) => {
console.log(res);
}
toggleLike = (e) => {
e.preventDefault();
const data = { model_id: this.props.id, model_type: 'comment' };
if (this.state.liked) {
removeLikeService(this.props.id, 'comment', this.removeLikeSuccess, this.error);
} else {
likeService(data, this.likeSuccess, this.error);
}
}
likeSuccess =() => {
this.toggleLikeState();
this.setState({ numberOfLikes: this.state.numberOfLikes += 1 });
}
removeLikeSuccess =() => {
this.toggleLikeState();
this.setState({ numberOfLikes: this.state.numberOfLikes -= 1 });
}
toggleLikeState =() => {
this.setState({ liked: !this.state.liked });
}
moreActionsClick =() => {
this.setState({ moreActionsActive: !this.state.moreActionsActive });
}
deleteReply = (replyId) => {
this.setState({ deletedReplyId: replyId });
deleteCommentService(replyId, this.deleteReplySuccess, this.error);
}
deleteReplySuccess =() => {
this.setState(prevState => ({ replies: prevState.replies.filter(reply => reply.id !== this.state.deletedReplyId) }));
}
ifEnterPressed = (e) => {
if (e.key === 'Enter') {
e.preventDefault();
this.postReply();
}
}
reportComment =() => {
const data = { model_id: this.props.id, model_type: 'comment' };
reportCommentService(data, this.reportCommentSuccess, this.error);
}
reportCommentSuccess = (res) => {
console.log(res);
}
render() {
let repliesList;
if (this.state.replies.length) {
repliesList = (this.state.replies.map((reply) => {
const { id, owner_id, content, owner_image_url, owner_full_name, ago, likes, liked } = reply;
return (
<Reply
key={id}
id={id}
authorId={owner_id}
title={content}
image={owner_image_url}
authorName={owner_full_name}
timeSinceComment={ago}
likes={likes}
liked={liked}
newComment={this.newCommentId}
deleteReply={this.deleteReply}
/>
);
}));
}
const commentClass = classNames('comment-container', {
'my-comment': this.props.myComment,
'comment-reported': this.state.reported,
});
let likeBtnText;
const numberOfLikes = this.state.numberOfLikes;
if (numberOfLikes > 0) {
likeBtnText = `${numberOfLikes} Likes`;
if (numberOfLikes === 1) {
likeBtnText = `${numberOfLikes} Like`;
}
} else {
likeBtnText = 'Like';
}
const likeBtnClass = classNames('like-btn', 'faux-btn', 'grey-link', 'h5', {
liked: this.state.liked,
});
let likeIconFill;
if (this.state.liked) {
likeIconFill = 'green';
} else {
likeIconFill = 'grey';
}
return (
<li className={commentClass}>
<div className="comment">
<Avatar image={this.props.image} />
<div className="body">
<div className="header">
<a href={`/user/${this.props.authorId}`} target="_blank" className="username green-link fw-medium">{this.props.authorName}</a>
<span className="h5 text-grey">{this.props.timeSinceComment}</span>
<Dropdown
size="S"
position="right"
onClick={this.moreActionsClick}
active={this.state.moreActionsActive}
handleClickOutside={this.moreActionsClick}
disableOnClickOutside={!this.state.moreActionsActive}
>
<DropdownSection>
{this.props.myComment &&
<button className="faux-btn dropdown-link" onClick={() => this.props.deleteComment(this.props.id)}>Delete comment</button>
}
</DropdownSection>
<DropdownSection>
<button className="faux-btn dropdown-link" onClick={() => this.reportComment(this.props.id)}>Report as inappropriate</button>
</DropdownSection>
</Dropdown>
</div>
<div className="comment-text"><p>{this.props.title}</p></div>
<div className="actions">
<button onClick={this.toggleLike} className={likeBtnClass}>
<IconWithText text={likeBtnText} iconName="thumb-up" iconSize="S" iconFill={likeIconFill} />
</button>
<button onClick={this.toggleReply} className="reply-btn faux-btn grey-link h5">
<IconWithText text="Reply" iconName="reply" iconSize="S" iconFill="grey" />
</button>
</div>
</div>
</div>
{this.state.replies.length > 0 &&
<div className="replies-container">
<ul className="replies-list faux-list no-margin-list">
{repliesList}
</ul>
</div>
}
{this.state.showReply &&
<div className="reply-to-comment-form">
<CommentForm
commentContent={this.handleInput}
postComment={(e) => { e.preventDefault(); this.postReply(); }}
formDisabled={this.state.replyBtnDisabled}
placeholder="Write a reply... press enter to submit"
btnText="Reply"
inputRef={(input) => { this.replyInput = input; }}
handleKeyPress={this.ifEnterPressed}
/>
</div>
}
</li>
);
}
}
Comment.propTypes = {
id: PropTypes.number,
authorId: PropTypes.number,
title: PropTypes.string,
image: PropTypes.string,
authorName: PropTypes.string,
timeSinceComment: PropTypes.string,
likes: PropTypes.array,
liked: PropTypes.bool,
replies: PropTypes.array,
myComment: PropTypes.bool,
deleteComment: PropTypes.func,
newCommentId: PropTypes.number,
reported: PropTypes.bool,
};