本文重要是根據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;*/ }