本案例在上一篇的案例(React组件之间通过Props传值的技巧(小案例,帮助体会理解props、state、受控组件和非受控组件等))的基础上加强功能和用户体验,但是当然还有很多需要改进的地方,后期一步步慢慢增强:
import React,{Component} from 'react'; import {render} from 'react-dom'; import './index.css'; 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() }) } this.setState({ content:'' }) }; handleUsernameHold=(event)=>{ localStorage.setItem('username',event.target.value) }; componentWillMount(){ const username=localStorage.getItem('username'); if(username){ this.setState({username}) } } 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> ) } } class CommentList extends Component{ constructor(){ super(); this.state={ items:[] } } render(){ return( <div> {this.props.items.map((item,index)=><Comment deleteItem={this.props.deleteItem} item={item} index={index} key={index}/>)} </div> ) } } class Comment extends Component{ constructor(){ super(); this.state={ timeString:'' } } handleTimeString=()=>{ const item=this.props.item; const duration=(+Date.now()-item.createTime)/1000; return duration>60?`${Math.round(duration/60)}分钟前`:`${Math.round(Math.max(duration,1))}秒前`; }; 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> ) } } class CommentApp extends Component{ constructor(){ super(); this.state={ items:[] } } handleSubmit=(item)=>{ 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> ) } } class Index extends Component{ render(){ return( <div> <CommentApp/> </div> ) } } render(<Index/>,document.getElementById('root'));
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; }