React 應用都是構(gòu)建在組件之上互躬。
組件可以將UI切分成一些的獨立的捺萌、可復用的部件,這樣你就只需專注于構(gòu)建每一個單獨的部件瞧柔。
組件從概念上看就像是函數(shù):
它可以接收任意的輸入值(稱之為“props”),并返回一個需要在頁面上展示的React元素睦裳。
一造锅、函數(shù)定義/類定義組件
注意: React 組件的名字必須定義為大寫開頭(比如以下的Welcome):因為利用 JSX 編寫 DOM 結(jié)構(gòu),可以用原生的 HTML 標簽廉邑,也可以直接像普通標簽一樣引用 React 組件哥蔚。這兩者約定通過大小寫來區(qū)分,小寫的字符串是 HTML 標簽蛛蒙,大寫開頭的變量是 React 組件糙箍。
1. 函數(shù)定義
定義一個組件最簡單的方式是使用JavaScript函數(shù):
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
該函數(shù)是一個有效的React組件,它接收一個單一的“props”對象并返回了一個React元素牵祟。我們之所以稱這種類型的組件為函數(shù)定義組件深夯,是因為從字面上來看,它就是一個JavaScript函數(shù)诺苹。
2. 類定義組件
也可以使用 ES6 class 來定義一個組件:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
一般來說咕晋,一個組件類由 extends Component 創(chuàng)建,并且提供一個 render 方法以及其他可選的生命周期函數(shù)筝尾、組件相關(guān)的事件或方法來定義捡需。
二办桨、組件渲染
在前面筹淫,我們遇到的React元素都只是DOM標簽:
const element = <div />;
然而,React元素也可以是用戶自定義的組件:
const element = <Welcome name="Sara" />;
當React遇到的元素是用戶自定義的組件,它會將JSX屬性作為單個對象傳遞給該組件损姜,這個對象稱之為“props”饰剥。
例如,這段代碼會在頁面上渲染出”Hello,Sara”:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
我們來回顧一下在這個例子中發(fā)生了什么:
- 我們對
<Welcome name="Sara" />
元素調(diào)用了ReactDOM.render()
方法。 - React將
{name: 'Sara'}
作為props
傳入并調(diào)用Welcome
組件摧阅。 -
Welcome
組件將<h1>Hello, Sara</h1>
元素作為結(jié)果返回汰蓉。 -
React DOM
將DOM
更新為<h1>Hello, Sara</h1>
。
警告:
組件名稱必須以大寫字母開頭棒卷。
例如顾孽,<div /> 表示一個DOM標簽,但 <Welcome /> 表示一個組件比规,并且在使用該組件時你必須定義或引入它若厚。
三、組合組件
組件可以在它的輸出中引用其它組件蜒什,這就可以讓我們用同一組件來抽象出任意層次的細節(jié)测秸。在React應用中,按鈕灾常、表單霎冯、對話框、整個屏幕的內(nèi)容等钞瀑,這些通常都被表示為組件沈撞。
例如,我們可以創(chuàng)建一個App組件仔戈,用來多次渲染W(wǎng)elcome組件:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
// 省略了const element = <App />;
ReactDOM.render(
<App />,
document.getElementById('root')
);
通常关串,一個新的React應用程序的頂部是一個App組件。但是监徘,如果要將React集成到現(xiàn)有應用程序中晋修,則可以從下而上使用像Button這樣的小組件作為開始,并逐漸運用到視圖層的頂部凰盔。
警告:
組件的返回值只能有一個根元素墓卦。這也是我們要用一個<div>來包裹所有<Welcome />元素的原因。
四户敬、提取組件
你可以將組件切分為更小的組件落剪,這沒什么好擔心的。
例如尿庐,來看看這個Comment組件:
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<img className="Avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
這個組件接收author
(對象)忠怖、text
(字符串)、以及date
(Date對象)作為props
抄瑟,用來描述一個社交媒體網(wǎng)站上的評論凡泣。
這個組件由于嵌套,變得難以被修改,可復用的部分也難以被復用鞋拟。所以讓我們從這個組件中提取出一些小組件骂维。
首先,我們來提取Avatar組件:
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
Avatar
作為Comment
的內(nèi)部組件贺纲,不需要知道是否被渲染航闺。因此我們將author
改為一個更通用的名字user
。
我們建議從組件自身的角度來命名props
猴誊,而不是根據(jù)使用組件的上下文命名潦刃。
現(xiàn)在我們可以對Comment組件做一些小小的調(diào)整:
function Comment(props) {
return (
<div className="Comment">
<div className="UserInfo">
<Avatar user={props.author} />
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
接下里,我們要提取一個UserInfo
組件懈叹,用來渲染Avatar
旁邊的用戶名:
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user} />
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
這可以讓我們進一步簡化Comment
組件:
function Comment(props) {
return (
<div className="Comment">
<UserInfo user={props.author} />
<div className="Comment-text">
{props.text}
</div>
<div className="Comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
提取組件一開始看起來像是一項單調(diào)乏味的工作福铅,但是在大型應用中,構(gòu)建可復用的組件完全是值得的项阴。當你的UI中有一部分重復使用了好幾次(比如滑黔,Button
、Panel
环揽、Avatar
)略荡,或者其自身就足夠復雜(比如,App
歉胶、FeedStory
汛兜、Comment
),類似這些都是抽象成一個可復用組件的絕佳選擇通今,這也是一個比較好的做法粥谬。
個人總結(jié)注意點:
- 構(gòu)造函數(shù)是唯一初始化state的地方
- 要更改狀態(tài)必須調(diào)用 this.setState({ });
- 如果用props和state來計算的值,接收函數(shù)而不是對象this.setState((prevState, props) => ({}));