React 小案例 用户评论

栏目: IOS · Android · 发布时间: 5年前

内容简介:1.用户可输入用户名2.输入评论内容3.点击发布

功能展示 :

1.用户可输入用户名

2.输入评论内容

3.点击发布

4.用户名和用户评论会被显示在列表里,第一个评论在最上面

5.且显示本条评论离现在过去了多长时间

6.鼠标放在事件上方可显示删除键,点击删除,删除当前

React 小案例 用户评论

总体结构:

1.components下创建comment文件夹

2.先创建用户输入组件:CommentInput

3.再创建单条用户评论视图:Comment

4.接着创建CommentList组件,将用户评论遍历渲染成评论列表

5.创建CommentApp组件,将CommentInput评论组件和CommentList列表组件渲染到一起

6.再index.js中引入展示

React 小案例 用户评论

用户输入组件:CommentInput 所有内容

1.创建用户名输入视图模板,

用ref获取input此DOM节点的值,

import {Component} from "react";
import React from "react";


class CommentInput extends Component{
    constructor(){
        super();
        this.state={
            //用户名
            username:'',
            //评论内容
            content:''
        }
    }

//监听用户输入用户名事件
    handleUsernameChange=(event)=>{
        this.setState({
            username:event.target.value
        })
    };
//监听用户输入评论事件
    handleContentChange=(event)=>{
        this.setState({
            content:event.target.value
        })
    };
    //点击发布事件
    handleSubmit=()=>{
        if(this.props.submit){
            this.props.submit({
                username:this.state.username,
                content:this.state.content,
                createTime:+new Date()
            })
        }
        //点击发布后将state中的评论区域重新至为空
        this.setState({
            content:''
        })
    };
//用户名框失去焦点
    handleUsernameHold=(event)=>{
        //将用户名数据存储到本地
        localStorage.setItem('username',event.target.value)
    };
    //将要装载,在render之前调用;
    componentWillMount(){
        //改变数据重新渲染前get获取用户名
        const username=localStorage.getItem('username');
        if(username){
            this.setState({username})
        }
    }
    //(装载完成),在render之后调用
    componentDidMount(){
        //用户刷新之后,用户名输入自动获取焦点
        this.input.focus();
    };

    render(){
        return(
            <div className='comment-input'>
                <div className='comment-field'>
                    <span className='comment-field-name'>用户名:</span>
                    <div className='comment-field-input'>
                        <input
                            ref={(input)=>this.input=input}
                            value={this.state.username}
                            onBlur={this.handleUsernameHold}
                            onChange={this.handleUsernameChange}
                        />
                    </div>
                </div>
                <div className='comment-field'>
                    <span className='comment-field-name'>评论内容:</span>
                    <div className='comment-field-input'>
                        <textarea
                            value={this.state.content}
                            onChange={this.handleContentChange}
                        />
                    </div>
                </div>
                <div className='comment-field-button'>
                    <button onClick={this.handleSubmit}>
                        发布
                    </button>
                </div>
            </div>
        )
    }
}
export default CommentInput//给input标签绑定 ref={(input)=>{this.input = input}}
//用来直接获取dom节点的值,此处将input的值赋给this.input,让外部能访问

复制代码

单条用户评论视图:Comment 所有内容

import {Component} from "react";
import React from "react";

class Comment extends Component{
    constructor(){
        super();
        this.state ={
            //初始化时间
            timeString:''
        }
    }

    //显示用户评论时间
    handleTimeString=()=>{
        const item=this.props.item;
        //当前时间减去用户创建时的时间
        const duration=(+Date.now()-item.createTime)/1000;
        //如果足够60就转成分,不够60就转成秒
        return duration>60?`${Math.round(duration/60)}分钟前`:`${Math.round(Math.max(duration,1))}秒前`;
    };

    //点击删除事件
    //将handleDelete事件传出,index传出
    handleDelete=()=>{
        if(this.props.deleteItem){
            this.props.deleteItem(this.props.index)
        }
    };

    render(){
        return(
            <div className='comment'>
                <div className='comment-user'>
                    <span className='comment-username'>{this.props.item.username} </span>:
                </div>
                <p>{this.props.item.content}</p>
                <span className="comment-delete" onClick={this.handleDelete}>删除</span>
                <span className="comment-createdtime">
                    {this.handleTimeString()}
                </span>
            </div>
        )
    }
}

export default Comment复制代码

评论列表 CommentList 所有内容

import {Component} from "react";
import React from "react";
import Comment from './Comment'

class CommentList extends Component{

    constructor(){
        super();
        this.state ={
            items:[]
        }
    }
//将数组列表传出,将deleteItem删除事件传出
    render(){
        return(
            <div>
                {this.props.items.map((item,index)=>
                    <Comment deleteItem={this.props.deleteItem}
                             item={item} index={index} key={index}
                    />)}
            </div>
        )
    }
}

export default CommentList复制代码

总组件CommentApp 的所有内容

import {Component} from "react";
import React from "react";
import CommentInput from './CommentInput'
import CommentList from './CommentList'


class CommentApp extends Component{
    constructor(){
        super();
        this.state ={
            items:[]
        }
    }
    //点击发送
    handleSubmit=(item)=>{
        //点击发送时push当前那条
        this.state.items.push(item);
        //更新数据列表
        this.setState({
            items:this.state.items
        });
        //用户刷新时保存当前数据
        localStorage.setItem('items',JSON.stringify(this.state.items))
    };
    //删除事件
    handleDelete=(index)=>{
        console.log(index);
        //点击删除,删除当前
        this.state.items.splice(index,1);
        //列表数据更新至最新
        this.setState({
            items:this.state.items
        });
        //删除后保存当下数据
        localStorage.setItem('items',JSON.stringify(this.state.items))
    };
    //装载前获取
    componentWillMount(){
        let items=localStorage.getItem('items');
        if(items){
            items=JSON.parse(items);
            this.setState({items})
        }
    };

    render(){
        return(
            <div className="wrapper">
                <CommentInput submit={this.handleSubmit} />
                <CommentList deleteItem={this.handleDelete} items={this.state.items}/>
            </div>
        )
    }
}

export default CommentApp

// window.localStorage
// 保存数据语法:
// localStorage.setItem("key", "value");
// 读取数据语法:
// var lastname = localStorage.getItem("key");
// 删除数据语法:
// localStorage.removeItem("key");复制代码

index.js下所有内容:

import React from 'react';
import ReactDOM from 'react-dom';
import CommentApp from "./components/comment/CommentApp";
import './index.css'


ReactDOM.render(<CommentApp/>, document.getElementById('root'));

复制代码

index.css下所有样式内容:

body {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  background-color: #fbfbfb;
}

.wrapper {
  width: 500px;
  margin: 10px auto;
  font-size: 14px;
  background-color: #fff;
  border: 1px solid #f1f1f1;
  padding: 20px;
}

/* 评论框样式 */
.comment-input {
  background-color: #fff;
  border: 1px solid #f1f1f1;
  padding: 20px;
  margin-bottom: 10px;
}

.comment-field {
  margin-bottom: 15px;
  display: flex;
}

.comment-field .comment-field-name {
  display: flex;
  flex-basis: 100px;
  font-size: 14px;
}

.comment-field .comment-field-input {
  display: flex;
  flex: 1;
}

.comment-field-input input,
.comment-field-input textarea {
  border: 1px solid #e6e6e6;
  border-radius: 3px;
  padding: 5px;
  outline: none;
  font-size: 14px;
  resize: none;
  flex: 1;
}

.comment-field-input textarea {
  height: 100px;
}

.comment-field-button {
  display: flex;
  justify-content: flex-end;
}

.comment-field-button button {
  padding: 5px 10px;
  width: 80px;
  border: none;
  border-radius: 3px;
  background-color: #00a3cf;
  color: #fff;
  outline: none;
  cursor: pointer;
}

.comment-field-button button:active {
  background: #13c1f1;
}

/* 评论列表样式 */
.comment-list {
  background-color: #fff;
  border: 1px solid #f1f1f1;
  padding: 20px;
}

/* 评论组件样式 */
.comment {
  position: relative;
  display: flex;
  border-bottom: 1px solid #f1f1f1;
  margin-bottom: 10px;
  padding-bottom: 10px;
  min-height: 50px;
}

.comment .comment-user {
  flex-shrink: 0;
}

.comment-username {
  color: #00a3cf;
  font-style: italic;
}

.comment-createdtime {
  padding-right: 5px;
  position: absolute;
  bottom: 0;
  right: 0;
  padding: 5px;
  font-size: 12px;
}

.comment:hover .comment-delete {
  color: #00a3cf;
}

.comment-delete {
  position: absolute;
  right: 0;
  top: 0;
  color: transparent;
  font-size: 12px;
  cursor: pointer;
}

.comment p {
  margin: 0;
  /*text-indent: 2em;*/
}

code {
  border: 1px solid #ccc;
  background: #f9f9f9;
  padding: 0px 2px;
}复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

解码宇宙

解码宇宙

(美) 塞费 / 隋竹梅 / 上海科技教育出版社 / 2010-4 / 26.00元

《解码宇宙:新信息科学看天地万物》:宇宙,或许就是一台庞大的计算机。这是查尔斯·塞费在《解码宇宙:新信息科学看天地万物》中对宇宙做出的结论。作者从信息的特点开始谈起,详细论述了信息论和量子计算,向我们展示了一种不可思议的拜占庭式宇宙的情景,涉及生命的本质、热力学、相对论、量子力学、黑洞、多重宇宙,直至宇宙的命运。《解码宇宙:新信息科学看天地万物》资料翔实,内容丰富多彩,思路清晰,观点明确,读后使人......一起来看看 《解码宇宙》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具