React中的五種組件形式

目前的前端開發(fā)主流技術(shù)都已經(jīng)往組件化方向發(fā)展了,而每學(xué)一種新的框架的時候祥国,最基礎(chǔ)的部分一定是學(xué)習(xí)其組件的編寫方式昵观。這就好像學(xué)習(xí)一門新的編程語言的時候,總是要從hello world開始一樣舌稀。而在React中啊犬,我們常用的組件編寫方式又有哪些呢?或者說各種不同的組件又可以分為幾類呢壁查?

無狀態(tài)組件

無狀態(tài)組件(Stateless Component)是最基礎(chǔ)的組件形式觉至,由于沒有狀態(tài)的影響所以就是純靜態(tài)展示的作用。一般來說睡腿,各種UI庫里也是最開始會開發(fā)的組件類別语御。如按鈕峻贮、標(biāo)簽、輸入框等应闯。它的基本組成結(jié)構(gòu)就是屬性(props)加上一個渲染函數(shù)(render)纤控。由于不涉及到狀態(tài)的更新,所以這種組件的復(fù)用性也最強(qiáng)碉纺。

const PureComponent = (props) => (
    <div>
        //use props
    </div>
)

無狀態(tài)組件的寫法十分簡單嚼黔,比起使用傳統(tǒng)的組件定義方式,我通常就直接使用ES6語法中提供的箭頭函數(shù)來聲明這種組件形式惜辑。當(dāng)然唬涧,如果碰到稍微復(fù)雜點的,可能還會帶有生命周期的hook函數(shù)盛撑。這時候就需要用到Class Component的寫法了碎节。

有狀態(tài)組件

在無狀態(tài)組件的基礎(chǔ)上,如果組件內(nèi)部包含狀態(tài)(state)且狀態(tài)隨著事件或者外部的消息而發(fā)生改變的時候抵卫,這就構(gòu)成了有狀態(tài)組件(Stateful Component)狮荔。有狀態(tài)組件通常會帶有生命周期(lifecycle),用以在不同的時刻觸發(fā)狀態(tài)的更新介粘。這種組件也是通常在寫業(yè)務(wù)邏輯中最經(jīng)常使用到的殖氏,根據(jù)不同的業(yè)務(wù)場景組件的狀態(tài)數(shù)量以及生命周期機(jī)制也不盡相同。

class StatefulComponent extends Component {

    constructor(props) {
        super(props);
        this.state = {
            //定義狀態(tài)
        }
    }

    componentWillMount() {
        //do something
    }
  
    componentDidMount() {
        //do something
    }
    ... //其他生命周期

    render() {
        return (
            //render
        );
    }
}

容器組件

在具體的項目實踐中姻采,我們通常的前端數(shù)據(jù)都是通過Ajax請求獲取的雅采,而且獲取的后端數(shù)據(jù)也需要進(jìn)一步的做處理。為了使組件的職責(zé)更加單一慨亲,引入了容器組件(Container Component)的概念婚瓜。我們將數(shù)據(jù)獲取以及處理的邏輯放在容器組件中,使得組件的耦合性進(jìn)一步地降低刑棵。

var UserListContainer = React.createClass({
  getInitialState: function() {
    return {
      users: []
    }
  },

  componentDidMount: function() {
    var _this = this;
    axios.get('/path/to/user-api').then(function(response) {
      _this.setState({users: response.data});
    });
  },

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

如上面這個容器組件巴刻,就是負(fù)責(zé)獲取用戶數(shù)據(jù),然后以props的形式傳遞給UserList組件來渲染蛉签。容器組件也不會在頁面中渲染出具體的DOM節(jié)點胡陪,因此,它通常就充當(dāng)數(shù)據(jù)源的角色碍舍。目前很多常用的框架柠座,也都采用這種組件形式。如:React Redux的connect(), Relay的createContainer(), Flux Utils的Container.create()等乒验。

高階組件

其實對于一般的中小項目來說愚隧,你只需要用到以上的這三種組件方式就可以很好地構(gòu)造出所需的應(yīng)用了。但是當(dāng)面對復(fù)雜的需求的時候,我們往往可以利用高階組件(Higher-Order Component)編寫出可重用性更強(qiáng)的組件狂塘。那么什么是高階組件呢录煤?其實它和高階函數(shù)的概念類似,就是一個會返回組件的組件荞胡÷栌唬或者更確切地說,它其實是一個會返回組件的函數(shù)泪漂。就像這樣:

const HigherOrderComponent = (WrappedComponent) => {
  return class WrapperComponent extends Component {
    render() {
      //do something with WrappedComponent
    }
  }
}

做為一個高階組件廊营,可以在原有組件的基礎(chǔ)上,對其增加新的功能和行為萝勤。我們一般希望編寫的組件盡量純凈或者說其中的業(yè)務(wù)邏輯盡量單一露筒。但是如果各種組件間又需要增加新功能,如打印日志敌卓,獲取數(shù)據(jù)和校驗數(shù)據(jù)等和展示無關(guān)的邏輯的時候慎式,這些公共的代碼就會被重復(fù)寫很多遍。因此趟径,我們可以抽象出一個高階組件瘪吏,用以給基礎(chǔ)的組件增加這些功能,類似于插件的效果蜗巧。

一個比較常見的例子是表單的校驗掌眠。

//檢驗規(guī)則,表格組件
const FormValidator = (WrappedComponent, validator, trigger) => {

   getTrigger(trigger, validator) {
      var originTrigger = this.props[trigger];
      
      return function(event) {
          //觸發(fā)驗證機(jī)制,更新狀態(tài)
          // do something ...
          originTrigger(event);
      }
  }

  var newProps = {
    ...this.props,
    [trigger]:   this.getTrigger(trigger, validator) //觸發(fā)時機(jī),重新綁定原有觸發(fā)機(jī)制
  };

  return <WrappedComponent  {...newProps} />
}

值得提一句幕屹,同樣是給組件增加新功能的方法蓝丙,相比于使用mixins這種方式高階組件則更加簡潔和職責(zé)更加單一。你如果使用過多個mixins的時候香嗓,狀態(tài)污染就十分容易發(fā)生迅腔,以及你很難從組件的定義上看出隱含在mixins中的邏輯。而高階組件的處理方式則更加容易維護(hù)靠娱。

另一方面,ES7中新的語法Decorator也可以用來實現(xiàn)和上面寫法一樣的效果掠兄。

function LogDecorator(msg) {
  return (WrappedComponent) => {
    return class LogHoc extends Component {
      render() {
        // do something with this component
        console.log(msg);
        <WrappedComponent {...this.props} />
      }
    }
  }
}

@LogDecorator('hello world')
class HelloComponent extends Component {
  
  render() {
    //...
  }
}

Render Callback組件

還有一種組件模式是在組件中使用渲染回調(diào)的方式像云,將組件中的渲染邏輯委托給其子組件。就像這樣:

import { Component } from "react";

class RenderCallbackCmp extends Component {
  constructor(props) {
    super(props);
    this.state = {
      msg: "hello"
    };
  }

  render() {
    return this.props.children(this.state.msg);
  }
}

const ParentComponent = () =>
  (<RenderCallbackCmp>
    {msg =>
      //use the msg
      <div>
        {msg}
      </div>}
  </RenderCallbackCmp>);

父組件獲取了內(nèi)部的渲染邏輯蚂夕,因此在需要控制渲染機(jī)制時可以使用這種組件形式迅诬。

總結(jié)

以上這些組件編寫模式基本上可以覆蓋目前工作中所需要的模式。在寫一些復(fù)雜的框架組件的時候婿牍,仔細(xì)設(shè)計和研究組件間的解耦和組合方式侈贷,能夠使后續(xù)的項目可維護(hù)性大大增強(qiáng)。

參考文檔

React Patterns - Render Callback
React Higher Order Components in depth

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末等脂,一起剝皮案震驚了整個濱河市俏蛮,隨后出現(xiàn)的幾起案子撑蚌,更是在濱河造成了極大的恐慌,老刑警劉巖搏屑,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件争涌,死亡現(xiàn)場離奇詭異,居然都是意外死亡辣恋,警方通過查閱死者的電腦和手機(jī)亮垫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伟骨,“玉大人饮潦,你說我怎么就攤上這事⌒粒” “怎么了继蜡?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長暑中。 經(jīng)常有香客問我壹瘟,道長,這世上最難降的妖魔是什么鳄逾? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任稻轨,我火速辦了婚禮,結(jié)果婚禮上雕凹,老公的妹妹穿的比我還像新娘殴俱。我一直安慰自己,他們只是感情好枚抵,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布线欲。 她就那樣靜靜地躺著,像睡著了一般汽摹。 火紅的嫁衣襯著肌膚如雪李丰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天逼泣,我揣著相機(jī)與錄音趴泌,去河邊找鬼。 笑死拉庶,一個胖子當(dāng)著我的面吹牛嗜憔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播氏仗,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼吉捶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起呐舔,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤币励,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后滋早,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體榄审,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年杆麸,在試婚紗的時候發(fā)現(xiàn)自己被綠了搁进。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡昔头,死狀恐怖饼问,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情揭斧,我是刑警寧澤莱革,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站讹开,受9級特大地震影響盅视,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜旦万,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一闹击、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧成艘,春花似錦赏半、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至秋冰,卻和暖如春仲义,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背剑勾。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工光坝, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人甥材。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像性含,于是被迫代替她去往敵國和親洲赵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 深入JSX date:20170412筆記原文其實JSX是React.createElement(componen...
    gaoer1938閱讀 8,050評論 2 35
  • 目前茴丰,react組件有三種寫法辜昵,分別是es5的createClass寫法饶套,es6的class寫法柜去,以及statel...
    ZoomFunc閱讀 1,625評論 0 1
  • 原教程內(nèi)容詳見精益 React 學(xué)習(xí)指南页眯,這只是我在學(xué)習(xí)過程中的一些閱讀筆記讹躯,個人覺得該教程講解深入淺出治筒,比目前大...
    leonaxiong閱讀 2,813評論 1 18
  • 高階組件是對既有組件進(jìn)行包裝展箱,以增強(qiáng)既有組件的功能腹殿。其核心實現(xiàn)是一個無狀態(tài)組件(函數(shù))独悴,接收另一個組件作為參數(shù),然...
    柏丘君閱讀 3,060評論 0 6
  • 假設(shè)你在讀一本好書,也就是說這是一本可以理解的書自沧。再假設(shè)最后你終于可以說:“我懂了坟奥!”再假設(shè)除了你看懂了全書之外,...
    重拾圖趣閱讀 735評論 0 2