文章目录
- 一、创建项目
- 二、项目实现
- 1. 整体效果
- 2. 需求分析
- 3. 开发思路
- 4. 主要代码
- ① App.jsx 根组件
- ② 添加评论组件 CommentAdd.jsx
- ③ 评论列表组件 CommentList.jsx
- ④ 评论项组件 CommentItem.jsx
- ⑤ 切换tab组件 ChangeTab.jsx
一、创建项目
- 全局安装脚手架:npm i -g create-react-app
- 使用脚手架创建项目:create-create-app my-react-demo
- 切换到项目目录:cd my-react-demo
- 运行项目:npm start
二、项目实现
1. 整体效果

2. 需求分析
- 可以输入并提交评论内容。
- 评价内容展示和按热度或时间排序。
- 删除评论列表中的评论。
3. 开发思路
- App 组件
- 添加评论组件 CommentAdd
- 评论列表组件 CommentList
- 评论Item项组件 CommentItem
- 切换tab键组件 ChangeTab
4. 主要代码
① App.jsx 根组件
import './index.css'
import React from 'react'
import avatar from './images/avatar.png'
import PropTypes from 'prop-types'
import CommentAdd from './CommentAdd'
import CommentList from './CommentList'
import CommentItem from './CommentItem'
import ChangeTab from './ChangeTab'
import { toHaveAccessibleDescription } from '@testing-library/jest-dom/dist/matchers'
// 定义函数组件
function HelloFn(){
// 定义事件回调函数
const clickHandler = () => {
console.log('事件被触发了!');
}
return (
// 绑定事件
<button onClick={clickHandler}>这是我的第一个函数组件!click me!</button>
)
}
// 定义类组件
class HelloC extends React.Component {
// 定义事件回调函数
clickHandler = () => {
console.log('事件被触发了!');
}
render(){
return (
<button onClick={this.clickHandler}>这是我的第一个类组件!click me!</button>
)
}
}
class App extends React.Component {
// 依赖的数据
state = {
// hot: 热度排序 time: 时间排序
tabs: [
{
id: 1,
name: '热度',
type: 'hot'
},
{
id: 2,
name: '时间',
type: 'time'
}
],
active: 'hot',
comments: [
{
id: 1,
author: '刘德华',
comment: '给我一杯忘情水',
time: new Date('2021-10-10 09:09:00'),
// 1: 点赞 0:无态度 -1:踩
attitude: 1
},
{
id: 2,
author: '周杰伦',
comment: '哎哟,不错哦',
time: new Date('2021-10-11 09:09:00'),
// 1: 点赞 0:无态度 -1:踩
attitude: 0
},
{
id: 3,
author: '五月天',
comment: '不打扰,是我的温柔',
time: new Date('2021-10-11 10:09:00'),
// 1: 点赞 0:无态度 -1:踩
attitude: -1
}
]
}
addComment = (comment) => {
// 将添加的评论追加到评论list上
const {comments} = this.state
comments.unshift(comment)
// 更新状态
this.setState({comments})
}
deleteComment = (index) => {
const {comments} = this.state
comments.splice(index,1)
this.setState({comments})
}
// 提供回调函数
changeCommentsSort = (newMsg) => {
this.setState({comments:newMsg})
}
render(){
return (
<div className="App">
{/* 渲染函数组件 */}
<HelloFn />
{/* 渲染类组件 */}
<HelloC />
<div className="comment-container">
{/* 评论数 */}
<div className="comment-head">
<span>{this.state.comments.length} 评论</span>
</div>
{/* 排序 */}
<div className="tabs-order">
{/* 子传父:① 首先需要父组件给子组件传一个回调函数,然后子组件调用 */}
<ChangeTab changeMsg={this.changeCommentsSort} comments={this.state.comments}/>
</div>
{/* 添加评论 */}
<CommentAdd addComment={this.addComment} />
{/* 评论列表 */}
<div className="comment-list">
<CommentList comments={this.state.comments} deleteComment={this.deleteComment} />
</div>
</div>
</div>
)}
}
export default
② 添加评论组件 CommentAdd.jsx
import React,{useRef} from 'react'
import PropTypes from 'prop-types'
import avatar from './images/avatar.png'
import { createRef } from 'react'
import {v4 as uuid} from 'uuid'
// 添加或删除评论 组件 
class CommentAdd extends React.Component {
    // 给组件对象指定state属性
    // 初始化状态
    constructor(props){
        super(props)
        this.state = {
            username: '游客 第' + uuid() + '号',
            content: '',
            myDiv: createRef()
          }
    }
    handleSubmit = () => {
        const comment = {
            author: this.state.username,
            comment: this.state.myDiv.current.value,
            time: new Date(),
            attitude: 0
        }
        if(comment.comment != ''){
            this.setState({username: '游客 第' + uuid() + '号'}) // 得到一个独一无二的uuid})
            // 添加评论 
            this.props.addComment(comment)
            // 清空输入数据
            this.setState({author: ''})
            this.state.myDiv.current.value = ''
        } else {
            alert('评论内容不能为空!')
        }
    }
   
    render(){
      const {comments} = this.state
      return (
        <div className="comment-send">
          <div className="user-face">
            <img className="user-head" src={avatar} alt="" />
          </div>
          <div className="textarea-container">
            <textarea
              cols="80"
              rows="5"
              placeholder="发条友善的评论"
              className="ipt-txt"
              ref={this.state.myDiv}
            />
            <button className="comment-submit" onClick={this.handleSubmit}>发表评论</button>
          </div>
          <div className="comment-emoji">
            <i className="face"></i>
            <span className="text">表情</span>
          </div>
      </div>
      )
    }
  }
  export default③ 评论列表组件 CommentList.jsx
import React, { Component } from "react";
import CommentItem from './CommentItem'
class CommentList extends Component {
    render(){
        const {comments,deleteComment} = this.props
        const display = comments.length === 0 ? 'block' : 'none'
        return (
            <div className="list-item">
                <h5 style={{display}}>暂无评论,点击添加评论!</h5>
               <ul>{
                    comments.map((comment,index)=> <li key={index}><CommentItem
                                                        comment={comment} 
                                                        index={index} 
                                                        deleteComment={deleteComment} />
                                                </li>) 
                    } 
                </ul>
            </div>
        )
    }
}
export default④ 评论项组件 CommentItem.jsx
import React, { Component } from "react";
import avatar from './images/avatar.png'
class CommentItem extends Component {
    handleDeleteComment = () => {
        const { comment, deleteComment, index } = this.props
        if (window.confirm(`确定删除${comment.author}的这条评论嘛?`)) {
            deleteComment(index)
        }
    }
    render() {
        const { comment } = this.props
        return (
            <div className="list-item">
              <div className="user-face">
                <img className="user-head" src={avatar} alt="" />
              </div>
              <div className="comment" >
                <div className="user">{comment.author}</div>
                <p className="text">{comment.comment}</p>
                <div className="info">
                  <span className="time">{comment.time.toLocaleString().replaceAll('/','-').replace('上午','').replace('下午','')}</span>
                  <span className="like liked">
                    <i className="icon" />
                  </span>
                  <span className="hate hated">
                    <i className="icon" />
                  </span>
                  <span className="reply btn-hover" onClick={this.handleDeleteComment}>删除</span>
                </div>
              </div>
            </div>
        )
    }
}
export default⑤ 切换tab组件 ChangeTab.jsx
import React from 'react'
// 切换tab 组件
class ChangeTab extends React.Component {
// 切换tab控件(按照时间、热度排序)
state = {
menuNum: 1
}
clickSortHandler = (num) => {
const {comments} = this.props
// 按热度排序(倒序)
if(num === 1){
comments.sort(this.compare('attitude',false))
this.props.changeMsg(comments)
}
// 按时间排序(倒序)
else if(num === 2) {
comments.sort(this.compare('time',false))
// ② 调用父组件传过来的回调函数,并注入参数(子传父)
this.props.changeMsg(comments)
}
this.setState({
menuNum: num
})
}
// 按某个字段进行排序
compare(prop,desc){
return function(a,b){
var value1 = a[prop]
var value2 = b[prop]
// 正序
if(desc == true){
return value1 - value2
}
// 倒序
else {
return value2 - value1
}
}
}
render(){
return (
<ul className="sort-container">
<li className={this.state.menuNum === 1 ? 'on' : ''} onClick={()=> this.clickSortHandler(1)}>按热度排序</li>
<li className={this.state.menuNum === 2 ? 'on' : ''} onClick={()=> this.clickSortHandler(2)}>按时间排序</li>
</ul>
)
}
}
export default
demo 完整代码已上传Github,欢迎 star ~
注: 此demo你可以学到:
- React 项目中的组件化开发思想
- 了解React项目的架构及组件拆分
- 了解React组件传值及状态更新
                










