詳解展示元件和容器元件的區別和應用

筱筱醬發表於2019-03-04

在使用React中,你是否會出現過一個檔案的程式碼很多,既存在應用資料的讀取和處理,又存在資料的顯示,而且每個元件還不能複用。

首先我們來看一個容器元件和展示元件一起的例子吧。

class TodoList extends React.Component{
    constructor(props){
        super(props);
        this.state ={
            todos:[]
        }
        this.fetchData = this.fetchData.bind(this);
    }
    componentDidMount(){
        this.fetchData();
    }
    fetchData(){
        fetch(`/api/todos`).then(data =>{
            this.setState({
                todos:data
            })
        })
    }
    render(){
        const {todos} = this.state;
        return (<div>
                <ul>
                    {todos.map((item,index)=>{
                        return <li key={item.id}>{item.name}</li>
                    })}
                </ul>
            </div>)
    }
}
複製程式碼

大家可以看到這個例子是沒有辦法複用的,因為資料的請求和資料的展示都在一個元件進行,要實現元件的複用,我們就需要將展示元件和容器元件分離出來。

具體程式碼如下:

//展示元件
class TodoList extends React.Component{
    constructor(props){
        super(props);
    }
    render(){
        const {todos} = this.props;
        return (<div>
                <ul>
                    {todos.map((item,index)=>{
                        return <li key={item.id}>{item.name}</li>
                    })}
                </ul>
            </div>)
    }

//容器元件
class TodoListContainer extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            todos:[]
        }
        this.fetchData = this.fetchData.bind(this);
    }
    componentDidMount(){
        this.fetchData();
    }
    fetchData(){
        fetch(`/api/todos`).then(data =>{
            this.setState({
                todos:data
            })
        })
    }
    render(){
        return (<div>
                <TodoList todos={this.state.todos} />    
            </div>)
    }
}

複製程式碼

當我們把元件分離成容器元件和展示元件這兩類時,你會發現他們能夠很方便的實現複用。

展示元件(Presentational Component)

  1. 關注頁面的展示效果(外觀)
  2. 內部可以包含展示元件和容器元件,通常會包含一些自己的DOM標記和樣式(style)
  3. 通常允許通過this.props.children方式來包含其他元件。
  4. 對應用程式的其他部分沒有依賴關係,例如Flux操作或store。
  5. 不用關心資料是怎麼載入和變動的。
  6. 只能通過props的方式接收資料和進行回撥(callback)操作。
  7. 很少擁有自己的狀態,即使有也是用於展示UI狀態的。
  8. 會被寫成函式式元件除非該元件需要自己的狀態,生命週期或者做一些效能優化。
Example:

Page,Header,Sidebar,UserInfo,List

容器元件(Container Component)

  1. 關注應用的是如何工作的
  2. 內部可以包含容器元件和展示元件,但通常沒有任何自己的DOM標記,除了一些包裝divs,並且從不具有任何樣式。
  3. 提供資料和行為給其他的展示元件或容器元件。
  4. 呼叫Flux操作並將它們作為回撥函式提供給展示元件。
  5. 往往是有狀態的,因為它們傾向於作為資料來源
  6. 通常使用高階元件生成,例如React Redux的connect(),Relay的createContainer()或Flux Utils的Container.create(),而不是手工編寫。
Example:

UserPage, FollowersSidebar, StoryContainer, FollowedUserList

優點(benifit)

  1. 展示和容器元件更好的分離,有助於更好的理解應用和UI
  2. 重用性高,展示元件可以用於多個不同資料來源。
  3. 展示元件就是你的調色盤,可以把他們放到單獨的頁面,在不影響應用程式的情況下,讓設計師調整UI。
  4. 這迫使您提取諸如側邊欄,頁面,上下文選單等“佈局元件”並使用this.props.children,而不是在多個容器元件中複製相同的標記和佈局。

何時引入容器元件

我建議你從開始建立元件時只使用展示元件,到最後會意識到你傳遞了很多props到中間元件,而這些中間元件根本不會用到他們接收到的這些props,僅僅是向下傳遞。而當這些子元件需要更多的資料時,你需要從新配置這些中間元件。這個時候就需要引入容器元件了。使用容器元件的方式,您可以將資料和行為通過props傳遞給葉子元件,而不必麻煩一些不相關的中間元件。

這是一個重構的過程,所以不比在第一次就做對。當你嘗試這種模式。在何時應抽取為容器元件你將會有一種直觀的感覺。就像您知道何時抽取函式一樣

二分法

重要的是你需要明白容器元件和展示元件之間不是技術上的區別,而是目的上的區別。

相比之下,這裡有幾個相關的(但不同的)技術區別:

  1. 有狀態【Stateful】和 無狀態【Stateless】:

    容器元件傾向於有狀態,展示元件傾向於無狀態,這不是硬性規定,因為容器元件和展示元件都可以是有狀態的。

  2. 類【Classes】 和 函式【Functions】:

    元件可以被申明成類或函式,函式元件定義簡單,但是他缺乏目前僅用於類的一些功能。雖然函式元件有很多限制,但是直到現在還有人使用,是因為函式元件容易理解,建議在不需要自己的state,lifecycle hooks,或效能優化的情況下使用函式元件。這些僅適用於類元件。

//我們將上邊的展示元件改寫成函式元件可以如下
function TodoList(props){
    return (<div>
              <ul>
                    {props.todos.map((item,index)=>{
                        return <li key={item.id}>{item.name}</li>
                    })}
                </ul>  
        </div>)
}

複製程式碼

可能很多人不清楚函式元件和類元件的區別,可以去React的官網看一下函式元件和類元件

  1. 純粹【Pure】 和 不純粹 【Impure】:

    純粹:輸入什麼就輸出什麼,不會再其中做相應的變動。可以被定義為類或函式,可以是無狀態或有狀態的,純元件的另一個重要方面是它們不依賴props或state中的深度變動,所以他們的渲染效能可以通過在shouldComponentUpdate()鉤子中進行淺層比較來優化,當前只有類可以定義shouldComponentUpdate()方法。

不管是展示元件還是容器元件都會有上面的二分特性。在我看來,展示元件往往是沒有狀態的純函式,而容器元件往往是有狀態的純類,這僅僅個人觀點並非規則。

當不重要或說很難分清時,不要把分離容器元件和展示元件當做教條,
如果你不確定該元件是容器元件還是展示元件是,就暫時不要分離,寫成展示元件吧。

譯文來源:https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0

相關文章