React 深入系列2:元件分類

iKcamp發表於2019-03-03

文:徐超,《React進階之路》作者

授權釋出,轉載請註明作者及出處


React 深入系列2:元件分類

React 深入系列,深入講解了React中的重點概念、特性和模式等,旨在幫助大家加深對React的理解,以及在專案中更加靈活地使用React。

React 元件有很多種分類方式,常見的分類方式有函式元件和類元件,無狀態元件和有狀態元件,展示型元件和容器型元件。好吧,這又是一篇咬文嚼字的文章。但是,真正把這幾組概念咬清楚、嚼明白後,對於頁面的元件劃分、元件之間的解耦是大有裨益的。

函式元件和類元件

函式元件(Functional Component )和類元件(Class Component),劃分依據是根據元件的定義方式。函式元件使用函式定義元件,類元件使用ES6 class定義元件。下面是函式元件和類元件的簡單示例:

// 函式元件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 類元件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
複製程式碼

上面的兩種寫法是等價的,但函式元件的寫法要比類元件簡潔,不過類元件比函式元件功能更加強大。類元件可以維護自身的狀態變數,即元件的state,類元件還有不同的生命週期方法,可以讓開發者能夠在元件的不同階段(掛載、更新、解除安裝),對元件做更多的控制。

類元件有這麼多優點,是不是我們在開發中應該首選使用類元件呢?其實不然。函式元件更加專注和單一,承擔的職責也更加清晰,它只是一個返回React 元素的函式,只關注對應UI的展現。函式元件接收外部傳入的props,返回對應UI的DOM描述,僅此而已。當然,如上面例子所示,使用只包含一個render方法的類元件,可以實現和函式元件相同的效果。但函式元件的使用可以從思想上迫使你在設計元件時多做思考,更加關注邏輯和顯示的分離,設計出更加合理的頁面上元件樹的結構。實際操作上,當一個元件不需要管理自身狀態時,可以把它設計成函式元件,當你有足夠的理由發現它需要“升級”為類元件時,再把它改造為類元件。因為函式元件“升級”為類元件是有一定成本的,這樣就會要求你做這個改造前更認真地思考其合理性,而不是僅僅為了一時的方便就使用類元件。

無狀態元件和有狀態元件

無狀態元件(Stateless Component )和有狀態元件(Stateful Component),劃分依據是根據元件內部是否維護state。無狀態元件內部不使用state,只根據外部元件傳入的props返回待渲染的React 元素。有狀態元件內部使用state,維護自身狀態的變化,有狀態元件根據外部元件傳入的props和自身的state,共同決定最終返回的React 元素。

很容易知道,函式元件一定是無狀態元件,類元件則既可以充當無狀態元件,也可以充當有狀態元件。但如上文所述,當一個元件不需要管理自身狀態時,也就是無狀態元件,應該優先設計為函式元件。

展示型元件和容器型元件

展示型元件(Presentational Component)和容器型元件(Container Component),劃分依據是根據元件的職責。

展示型元件的職責是:元件UI長成什麼樣。展示型元件不關心元件使用的資料是如何獲取的,以及元件資料應該如何修改,它只需要知道有了這些資料後,元件UI是什麼樣子的即可。外部元件通過props傳遞給展示型元件所需的資料和修改這些資料的回撥函式,展示型元件只是它們的使用者。展示型元件一般是無狀態元件,不需要state,因為展示型元件不需要管理資料,但當展示型元件需要管理自身的UI狀態時,例如控制元件內部彈框的顯示與隱藏,是可以使用state的,這時的state屬於UI state。既然大部分情況下展示型元件不需要state,應該優先考慮使用函式元件實現展示型元件。

容器型元件的職責是:元件資料如何工作。容器型元件需要知道如何獲取子元件所需資料,以及這些資料的處理邏輯,並把資料和邏輯通過props提供給子元件使用。容器型元件一般是有狀態元件,因為它們需要管理頁面所需資料。

例如,下面的例子中,UserListContainer是一個容器型元件,它獲取使用者列表資料,然後把使用者列表資料傳遞給展示型元件UserList,由UserList負責UI的展現。

class UserListContainer extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      users: []
    }
  }
  
  componentDidMount() {
    var that = this;
    fetch(`/path/to/user-api`).then(function(response) {
      response.json().then(function(data) {
        that.setState({users: data})
      });
    });
  }

  render() {
    return (
      <UserList users={this.state.users} />
    )
  }
}

function UserList(props) {
  return (
    <div>
      <ul className="user-list">
        {props.users.map(function(user) {
          return (
            <li key={user.id}>
              <span>{user.name}</span>
            </li>
          );
        })}
      </ul>
    </div>
  )  
}
複製程式碼

展示型元件和容器型元件是可以互相巢狀的,展示型元件的子元件既可以包含展示型元件,也可以包含容器型元件,容器型元件也是如此。例如,當一個容器型元件承擔的資料管理工作過於複雜時,可以在它的子元件中定義新的容器型元件,由新元件分擔資料的管理。展示型元件和容器型元件的劃分完全取決於元件所做的事情。

總結

通過上面的介紹,可以發現這三組概念有很多重疊部分。這三組概念都體現了關注點分離的思想:UI展現和資料邏輯的分離。函式元件、無狀態元件和展示型元件主要關注UI展現,類元件、有狀態元件和容器型元件主要關注資料邏輯。但由於它們的劃分依據不同,它們並非完全等價的概念。它們之間的關聯關係可以歸納為:函式元件一定是無狀態元件,展示型元件一般是無狀態元件;類元件既可以是有狀態元件,又可以是無狀態元件,容器型元件一般是有狀態元件。

下篇預告:

React 深入系列3:State 和 Props


新書推薦《React進階之路》

作者:徐超

畢業於浙江大學,碩士,資深前端工程師,長期就職於能源物聯網公司遠景智慧。8年軟體開發經驗,熟悉大前端技術,擁有豐富的Web前端和移動端開發經驗,尤其對React技術棧和移動Hybrid開發技術有深入的理解和實踐經驗。

React 深入系列2:元件分類

React 深入系列2:元件分類

React 深入系列2:元件分類

2019年,iKcamp原創新書《Koa與Node.js開發實戰》已在京東、天貓、亞馬遜、噹噹開售啦!

相關文章