四、元件化和屬性(props)
元件允許您將UI拆分為獨立的可重用的部分,並單獨地考慮每個部分。
從概念上講,元件就像JavaScript函式。 它們接受任意輸入(稱為“props”),並返回應該出現在螢幕上的React元素。
功能性元件(functional)和類元件(class component)
定義元件的最簡單的方法是編寫JavaScript函式:
function Welcome(props) {
return <h1>hello, {props.name}</h1>
}
此函式是個有效的React元件,因為它接收一個帶有資料的“props”物件作為引數,並返回一個React元素。 我們把這樣的元件稱為“功能性元件(functional)”,因為它們是個JavaScript函式。
您還可以使用ES6類來定義元件:
class Welcome extends React.Component {
render() {
return <h1>hello, {this.props.name}</h1>;
}
}
上述兩個元件從React的角度來看是等效的。
類元件有一些額外的功能,我們將在下面的章節中討論。 在那之前,我們將簡單地使用功能元件。
渲染一個元件
以前,我們只遇到使用DOM標籤的React元素:
const element = <div />;
但是,元素也可以表示使用者自定義的元件:
const element = <Welcome name="zhangyatao" />;
當React看到表示使用者定義元件的元素時,它將該JSX標籤的所以屬性放到一個物件中傳遞給元件。 我們稱這個物件為“props”。
例如,此程式碼在頁面上呈現“Hello,zhangyatao”:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="zhangyatao" />;
ReactDOM.render(
element,
document.getElementById(`root`)
);
讓我們回顧一下在這個例子中發生了什麼:
-
首先呼叫
ReactDOM.render()
方法,並處理<Welcome name =“zhangyatao”/>
元件。 -
React使用
{name:`zhangyatao`}
作為props呼叫Welcome
元件。 -
我們的
Welcome
元件返回一個<h1> Hello,zhangyatao </ h1>
元素作為結果。 -
React DOM有效地根據
<h1> Hello,zhangyatao </ h1>
來更新DOM。
警告
元件名稱始終使用·首字母大寫·。
例如,<div />
表示一個DOM標籤,但<Welcome />
表示一個元件,並要求Welcome
必須和ReactDOM在一個作用域內。
編寫元件
元件可以在其返回值中引用去其他元件。 這允許我們對任何級別的細節使用相同的元件抽象。 一個按鈕,一個表單,一個對話方塊,一個螢幕:在React應用程式中,所有這些通常表示為元件。
例如,我們可以建立一個App元件,讓它渲染多個Welcome
元件:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="zhangyatao" />
<Welcome name="jiangyanyun" />
<Welcome name="pangyun" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById(`root`)
);
這個新的React應用程式在頂部有一個單獨的App
元件。
但是,如果您將React整合到現有應用程式中,則可以使用Button這樣的小元件自下而上開始逐步向上到達檢視層次結構的頂部。
警告
引用多個元件時必須包裹在一個根元素中返回。 這就是為什麼我們新增了一個<div>
來包含所有<Welcome />
元素。
抽離元件
永遠不要害怕將元件拆分成更小的元件。
例如,考慮這個Comment
元件:
import React from `react`;
import ReactDOM from `react-dom`;
function formatDate(date) {
return date.toISOString();
}
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>
);
}
ReactDOM.render(
<Comment author={{
avatarUrl: `https://ss0.bdstatic.com/7Ls0a8Sm1A5BphGlnYG/sys/portrait/item/3ae1dc06.jpg`,
name: `zhangyatao`
}} text={`我的名字叫張亞濤`} date={new Date()}/>,
document.getElementById(`root`)
);
它接受author
(作者),text
(內容)和date
(日期)作為props,用來描述社交媒體網站上的評論。
這個元件可能很難改變,因為所有的巢狀,它也很難重用其中的單個部分。 讓我們從中提取幾個元件。
首先,我們將提取avatar
:
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
avatar
不需要知道它正在Comment
中呈現。 這就是為什麼我們給它的prop一個更通用的名稱:user
而不是author
。
我們建議從元件自己的角度來命名props,而不是使用它的上下文。
我們現在可以對Comment
元件做一點點簡化:
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 uer={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>
);
}
最終的程式碼如下:
import React from `react`;
import ReactDOM from `react-dom`;
function formatDate(date) {
return date.toISOString();
}
function Avatar(props) {
return (
<img className="Avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={props.user}/>
<div className="UserInfo-name">
{props.user.name}
</div>
</div>
);
}
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>
);
}
ReactDOM.render(
<Comment author={{
avatarUrl: `https://ss0.bdstatic.com/7Ls0a8Sm1A5BphGlnYG/sys/portrait/item/3ae1dc06.jpg`,
name: `zhangyatao`
}} text={`我的名字叫張亞濤`} date={new Date()}/>,
document.getElementById(`root`)
);
讓一個個可重用元件在大型應用程式中交付使用的過程中,抽離元件起初可能看起來像又髒又累的活兒。 所以有一個好的經驗法則:如果UI的一部分被使用了好幾次(按鈕,皮膚,頭像),或者內部比較複雜的東西(App,FeedStory,評論),一個可重用的元件對它來說可以達到最大的發揮空間。
Props是隻讀的
無論是將元件宣告為功能元件還是類元件,它都不能修改自己的props。 考慮這個計算引數總和的函式:
function sum(a, b) {
return a + b;
}
這樣的函式被稱為“純函式”
,因為它們不會改變它們的引數值,並且對於相同的輸入總是返回相同的結果。
相反,這個函式是不純的,因為它改變自己的引數:
function withdraw(account, amount) {
account.total -= amount;
}
React非常靈活,但它有一個嚴格的規則:
所有React元件必須像它們的porps的純函式那樣執行。
當然,應用中的UI大部分是動態的,隨時間變化。 在下一節中,我們將介紹一個“state”的新概念。 狀態允許React元件響應使用者操作,網路響應和其他任何內容,隨時間更改其輸出,而不違反此規則。