本文重要是根據react小書上的一個很簡單的例子改編的,加上自己的學習理解,希望可以通過實際案例讓大家對概念有更清晰的理解,當然也希望能一塊學習。
import React,{Component} from 'react';
import {render} from 'react-dom';
import './comprehensive.css'
/*
* 一個重要問題知識點:
*可以看到這個CommentInput組件和CommentList組件都是兩個子組件,但是開始是兩個毫不相干的子組件,我們想讓他們建立連接,但是同為兩個子級組件之間是沒法直接傳值的,這時候他們的父級組件CommentApp就起到了一個橋梁的作用
* ->當用戶點擊發布按鈕的時候我們將CommentInput中的state當中最新的評論數據傳遞給父級組件CommentApp,然后從父級組件CommentApp傳給子組件CommentList進行渲染
*
* 問題來了:那么子級CommentInput怎么向父級CommentApp傳值呢?
* 父組件CommentApp只需要通過props給子組件CommentInput傳入一個回調函數,當用戶點擊發布按鈕的時候CommentInput調用props中的回調函數並將state傳入該函數即可
*/
//這個是commentInput子組件,負責用戶輸入的區域
class CommentInput extends Component{
//這里初始化一個state值來保存用戶名(username)值和評論內容(content)
//但是自行設置一個初始化的參數之后就不能從界面中輸入了,因為輸入控件被設置成了固定值,永遠以設置的為准。
constructor(){
super();
this.state={
username:'',
content:''
}
}
//為了解決被控問題,所以就要setState一下,監聽輸入框中的onChange事件,獲取到用戶輸入的內容之后用setState更新一下狀態,這樣input里邊的內容才會發生改變。下同
handleUsernameChange=(event)=>{
this.setState({
//這里我們通過event.target.value獲取到輸入框中的內容,並通過setState把它設置到state.username中,這樣內容就會更新,輸入就再沒問題了。下同
username:event.target.value
})
};
handleContentChange=(event)=>{
this.setState({
content:event.target.value
})
};
handleSubmit=()=>{
//這個方法判斷props中是否傳入了onSubmit屬性,有的話就用該函數,並把用戶輸入的數據都傳給該函數
if(this.props.onSubmit){
const {username,content}=this.state;
this.props.onSubmit({
username,content
})
}
//增加用戶體驗,提交后清空原來的值
this.setState({
username:'',
content:''
})
};
render(){
return(
<div className="comment-input">
<div className="comment-field">
<span className="comment-field-name">用戶名:</span>
<div className="comment-field-input">
//上邊是設置state值初始化,這里取值就用this.state.XXX,下邊的評論內容同理
//這里加上onChange事件就是為了監聽input的值是否發生改變,從而更新狀態
<input type="text" value={this.state.username}
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{
//這里給組件加上defaultProps防止comments不傳而報錯的情況
static defaultProps={
comments:[]
};
render(){
return(
<div>
//遍歷數組將值傳遞給Comment
{this.props.comments.map((comment,index)=>{
return <Comment comment={comment} key={index}/>
})}
</div>
)
}
}
//評論列表中的每個單獨項,這個組件被commentList所用
class Comment extends Component{
render(){
return(
//接收到CommentList傳遞過來的每一條數據然后將其渲染在頁面上
<div className="comment">
<div className="comment-user">
<span className="comment-username">{this.props.comment.username}</span>
</div>
<p>{this.props.comment.content}</p>
</div>
)
}
}
//評論功能整體用這個組件包起來,包括commentInput區域和commentList區域
class CommentApp extends Component{
//在這里初始化一個數組,來保存所有的評論數據,並且通過props把它傳遞給CommentList
constructor(){
super();
this.state={
comments:[]
}
}
handleSubmitComment=(comment)=>{
//當用戶發布評論的時候就把數據插入到comment中,然后通過setState將數據更新到頁面上
this.state.comments.push(comment);
this.setState({
comments:this.state.comments
})
};
render(){
return(
<div className="wrapper">
//內部引入需要的子組件
//在這個父組件里給CommentInput子組件傳入一個onSubmit的屬性,這個屬性是CommentApp的一個方法,這樣CommentInput就可以調用this.props.onSubmit(...)將數據傳遞給CommentApp
<CommentInput onSubmit={this.handleSubmitComment}/>
<CommentList comments={this.state.comments}/>
</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 { display: flex; border-bottom: 1px solid #f1f1f1; margin-bottom: 10px; padding-bottom: 10px; min-height: 50px; } .comment .comment-user { flex-shrink: 0; } .comment span { color: #00a3cf; font-style: italic; } .comment p { margin: 0; /*text-indent: 2em;*/ }
