Component 規劃
評論功能整架構
向父元件傳遞數據
數據傳遞順序
當使用可點擊發布按鈕,將 CommentInput 的 state 當中最金的數據傳給父組件 ComponentApp,ComponentApp 會把這組數據傳給 CommentList 進行渲染。
如何傳遞數據
CommentInput
如何向 CommentApp
傳遞數據?父組件 CommentApp
只要透過 props 給子組件 CommentInput
傳入一個 callback function,當使用者點擊發布按鈕時,CommentInput
調用 props 中的 callback function 並將 state 傳入該函數即可。
在 CommentApp
中的 CommentInput
傳入 onSubmit
屬性,這個屬性值是 CommentApp
的一個方法 handleSubmitComment
。這樣 CommentInput
可調用 this.props.onSubmit(...)
把數據傳給 CommentApp
。
React 小書 評論功能
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import CommentApp from './CommentApp'
import './index.css'
ReactDOM.render(
<CommentApp />,
document.getElementById('root')
)
CommentApp.js
import React, { Component } from 'react'
import CommentInput from './CommentInput'
import CommentList from './CommentList'
class CommentApp extends Component {
constructor() {
super()
this.state = {
comments: []
}
}
handleSubmitComment (comment) {
console.log(comment)
if (!comment) return
if (!comment.username) return alert('請輸入使用者名稱')
if (!comment.content) return alert('請輸入內容')
this.state.comments.push(comment)
this.setState({
comments: this.state.comments
})
}
render() {
return (
<div className='wrapper'>
<CommentInput onSubmit={this.handleSubmitComment.bind(this)}/>
<CommentList comments={this.state.comments}/>
</div>
)
}
}
export default CommentApp
CommentInput.js
import React, { Component } 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.onSubmit) {
const { username, content } = this.state
this.props.onSubmit({username, content})
}
this.setState({ content: '' })
}
render () {
return (
<div className='comment-input'>
<div className='comment-field'>
<span className='comment-field-name'>使用者名稱:</span>
<div className='comment-field-input'>
<input
value={this.state.username}
onChange={this.handleUsernameChange.bind(this)} />
</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.bind(this)} />
</div>
</div>
<div className='comment-field-button'>
<button onClick={this.handleSubmit.bind(this)}>
發布
</button>
</div>
</div>
)
}
}
export default CommentInput
CommentList.js
import React, { Component } from 'react'
import Comment from './Comment'
class CommentList extends Component {
static defaultProps = {
comments: []
}
render() {
// const comments = [
// {username: 'Jerry', content: 'Hello'},
// {username: 'Tomy', content: 'World'},
// {username: 'Lucy', content: 'Good'}
// ]
// return (
// <div>
// {comments.map((comment, i) =>
// <Comment comment={comment} key={i} /> )}
// </div>
// )
return (
<div>
{this.props.comments.map((comment, i) =>
<Comment comment={comment} key={i} />
)}
</div>
)
}
}
export default CommentList
Comment.js
import React, { Component } from 'react'
class Comment extends Component {
render () {
return (
<div className='comment'>
<div className='comment-user'>
<span>{this.props.comment.username} </span>:
</div>
<p>{this.props.comment.content}</p>
</div>
)
}
}
export default Comment
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 {
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;*/
}
總結
- 實現功能前先理解、分析需求、劃分組件。劃分組件的基本原則:可複用性、可維護性。
- 受控組件:若 React.js 中的
<input />
,<textarea />
,<select />
等元素的值,如果受到 React.js 控制就是受控組件。 - 組件之間透過
props
通過父元素傳遞數據的技巧。