React從入門到精通系列之(4)元件化和Props傳遞

張亞濤發表於2016-12-13

四、元件化和屬性(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`)
);

讓我們回顧一下在這個例子中發生了什麼:

  1. 首先呼叫ReactDOM.render()方法,並處理<Welcome name =“zhangyatao”/>元件。

  2. React使用{name:`zhangyatao`}作為props呼叫Welcome元件。

  3. 我們的Welcome元件返回一個<h1> Hello,zhangyatao </ h1>元素作為結果。

  4. 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元件響應使用者操作,網路響應和其他任何內容,隨時間更改其輸出,而不違反此規則。

相關文章