[翻譯]React組件模式

原文地址: https://medium.com/teamsubchannel/react-component-patterns-e7fb75be7bb0
做者: William Whatley
摘要:本文介紹了4種組件類型:容器組件、展現組件、高階組件和渲染回調。

封面圖

今天,我想花一點時間來分享我學習React組件模式的經驗。這個想法來源於一次聚會時的技術交流。組件是React的核心,所以有必要去理解如何使用它們。react

如下的例子都脫胎於Michael Chan gave on React component patterns這個視頻的思想。我很是推薦大家看一看。git

什麼是組件?

reactjs.org上說:「組件可以將你的界面分割成獨立且可複用的小塊,而這些小組件都是互相獨立的。」github

當你第一次執行npm install react命令的時候,你獲得了一個組件以及相關的API。與JS的函數相似,一個組件接收輸入,叫作props。而後返回React元素,做用是描述UI界面的樣式。這就是React做爲聲明式API的表現形式,由於你只須要告訴它你想要展現的UI,剩下的React都會幫你完成。npm

對於聲明式API的概念,你能夠想象成滴滴打車的場景——告訴司機你的目的地,接着讓他來完成開車的工做。而命令式API不一樣,你將完成全部任務,既是乘客,也是司機。segmentfault

組件API

當你在安裝好React後,獲得了5類API:
api類型api

  • render
  • state
  • props
  • context
  • lifecycle events

儘管寫組件時能夠把上面全部的API都使用一遍,可是你很快會發現一些組件只須要用到某些API,而另外一些組件也只會使用另外一些特定的API。而這兩類組件每每被劃分爲有狀態與無狀態組件兩大類。有狀態的組件會有表明性地使用有狀態API:render、state和生命週期。但無狀態組件只會使用render、props和context。
有狀態與無狀態數組

以上就是咱們在介紹組件模式前所須要的知識鋪墊。組件模式是設計組件的最佳實踐,最初是把組件切割成數據/邏輯層和UI/展現層。經過拆分組件的職責,可以設計出更容易複用、更內聚的組件,從而組裝成複雜的UI界面。這個特性對於構建可擴展的應用時是很是重要的。react-router

組件模式

經常使用的組件模式有:dom

  • 容器組件
  • 展現組件
  • 高階組件HOC
  • 渲染回調

容器組件

「容器組件的做用是獲取數據和渲染子組件。」——Jason Bonta
容器組件函數

藍色表明容器組件,灰色表明展現用的子組件

容器組件使用了有狀態的API,封裝了數據邏輯層。經過使用生命週期,你能夠鏈接Redux或Flux等狀態管理庫,而後把數據和回調函數看成props傳遞給子組件。在容器組件的render方法中,你能夠用子展現組件來拼裝UI界面。容器組件每每都設計成一個類組件,而不是純函數組件,爲的就是可以使用全部有狀態的API。

在下面的例子中,咱們有一個名爲Greeting的有狀態的類組件,包括componentDidMount()render方法。

class Greeting extends React.Component {
  constructor() {
    super();
    this.state = {
      name: "",
    };
  }

  componentDidMount() {
    // AJAX
    this.setState(() => {
      return {
        name: "William",
      };
    });
  }

  render() {
    return (
      <div>
        <h1>Hello! {this.state.name}</h1>
      </div>
    );
  }
}

這時,這個組件僅僅是一個有狀態的類組件。爲了讓它成爲一個真正的容器組件,咱們要把UI部分放進一個展現組件中。下面就來說講展現組件。

展現組件

展現組件使用到props、render和context這些無狀態API,而且能夠寫成更簡潔優雅的函數式無狀態組件。

const GreetingCard = (props) => {
  return (
    <div>
      <h1>Hello! {props.name}</h1>
    </div>
  )
}

展現組件只能從父級組件或容器組件傳來的props中接收數據和回調函數。
展現組件

藍色表明展現組件,而灰色表明容器組件。

容器組件和展現組件合併起來後,封裝成了一個真正被使用的組件:

const GreetingCard = (props) => {
  return (
    <div>
      <h1>{props.name}</h1>
    </div>
  )
}

class Greeting extends React.Component {
  constructor() {
    super();
    this.state = {
      name: "",
    };
  }

  componentDidMount() {
    // AJAX
    this.setState(() => {
      return {
        name: "William",
      };
    });
  }

  render() {
    return (
      <div>
       <GreetingCard name={this.state.name} />
      </div>
    );
  }
}

如你所見,我把Greeting類組件中的UI部分移除,放入一個無狀態的函數組件中。固然,這只是一個簡單的例子,但對於複雜的應用來講,這是最基本的作法。

高階組件(HOC)

高階組件就是一個把組件看成參數,而且返回新組件的函數。

它的強大之處在於可以給任意數量的組件提供數據源,而且能夠被用來實現邏輯複用。用react-router-v4或Redux舉個例子。使用react-router-v4時,你可使用withRouter()來繼承傳給組件的props。而使用Redux時,你經過使用connect({})()來把actions看成props傳遞給子組件。
HOC

虛線表示的是高階組件,它可以返回一個新的組件。

看看這個例子:

import {withRouter} from 'react-router-dom';

class App extends React.Component {
  constructor() {
    super();
    this.state = {path: ''}
  }
  
  componentDidMount() {
    let pathName = this.props.location.pathname;
    this.setState(() => {
      return {
        path: pathName,
      }
    })
  }
  
  render() {
    return (
      <div>
        <h1>Hi! I'm being rendered at: {this.state.path}</h1>
      </div>
    )
  }
}

export default withRouter(App);

當導出個人組件時,我使用react-router-v4提供的withRouter()方法把它包裹起來。而在App的componentDidMount()生命週期中,我經過this.props.location.pathname來更新狀態。在被withRouter()包裹後,個人類組件如今能夠經過props訪問react-router-v4的方法,就像this.props.location.pathname。像這樣的例子,實在是不勝枚舉。

渲染回調(render callbacks)

與高階組件相似,渲染回調或者說渲染props都是用來實現組件邏輯複用功能。相比較於大多數開發者使用的高階組件,渲染回調也有本身的優點。具體優點能夠閱讀Michael Jackson所寫的這個視頻——《Never write another HOC.》。視頻中所講的關鍵點就是渲染回調可以減小命名空間的衝突而且解釋邏輯的來源。
渲染回調

藍色虛線表明渲染回調函數。
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  increment = () => {
    this.setState(prevState => {
      return {
        count: prevState.count + 1,
      };
    });
  };

  render() {
    return (
      <div onClick={this.increment}>{this.props.children(this.state)}</div>
    );
  }
}

class App extends React.Component {
  render() {
    return (
      <Counter>
        {state => (
          <div>
            <h1>The count is: {state.count}</h1>
          </div>
        )}
      </Counter>
    );
  }
}

在上面的Counter類中,我在render裏使用了this.props.children,而後把this.state看成參數傳給這個函數。以後在App類中,我把想要展現的組件用Counter組件包裹起來,這樣就能使用Counter的代碼邏輯了。render函數的返回結果是代碼28行,在那裏我經過{state => ()}自動獲取到Counter的state。

感謝您的閱讀

我很樂意接受你們的意見來使我成長。我對React組件模式的看法還不夠成熟,因此我也算是在寫做中學習吧。

查看更多我翻譯的Medium文章請訪問:
項目地址: https://github.com/WhiteYin/translation
SF專欄: https://segmentfault.com/blog/yin-translation
相關文章
相關標籤/搜索