React 中的五種組件形式

目前的前端開發主流技術都已經往組件化方向發展了,而每學一種新的框架的時候,最基礎的部分必定是學習其組件的編寫方式。這就好像學習一門新的編程語言的時候,老是要從hello world開始同樣。而在React中,咱們經常使用的組件編寫方式又有哪些呢?或者說各類不一樣的組件又能夠分爲幾類呢?javascript

無狀態組件

無狀態組件(Stateless Component)是最基礎的組件形式,因爲沒有狀態的影響因此就是純靜態展現的做用。通常來講,各類UI庫裏也是最開始會開發的組件類別。如按鈕、標籤、輸入框等。它的基本組成結構就是屬性(props)加上一個渲染函數(render)。因爲不涉及到狀態的更新,因此這種組件的複用性也最強。前端

const PureComponent = (props) => (
    <div> //use props </div>
)複製代碼

無狀態組件的寫法十分簡單,比起使用傳統的組件定義方式,我一般就直接使用ES6語法中提供的箭頭函數來聲明這種組件形式。固然,若是碰到稍微複雜點的,可能還會帶有生命週期的hook函數。這時候就須要用到Class Component的寫法了。java

有狀態組件

在無狀態組件的基礎上,若是組件內部包含狀態(state)且狀態隨着事件或者外部的消息而發生改變的時候,這就構成了有狀態組件(Stateful Component)。有狀態組件一般會帶有生命週期(lifecycle),用以在不一樣的時刻觸發狀態的更新。這種組件也是一般在寫業務邏輯中最常用到的,根據不一樣的業務場景組件的狀態數量以及生命週期機制也不盡相同。react

class StatefulComponent extends Component {

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

    componentWillMount() {
        //do something
    }

    componentDidMount() {
        //do something
    }
    ... //其餘生命週期

    render() {
        return (
            //render
        );
    }
}複製代碼

容器組件

在具體的項目實踐中,咱們一般的前端數據都是經過Ajax請求獲取的,並且獲取的後端數據也須要進一步的作處理。爲了使組件的職責更加單一,引入了容器組件(Container Component)的概念。咱們將數據獲取以及處理的邏輯放在容器組件中,使得組件的耦合性進一步地下降。ios

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} />); } });複製代碼

如上面這個容器組件,就是負責獲取用戶數據,而後以props的形式傳遞給UserList組件來渲染。容器組件也不會在頁面中渲染出具體的DOM節點,所以,它一般就充當數據源的角色。目前不少經常使用的框架,也都採用這種組件形式。如:React Redux的connect(), Relay的createContainer(), Flux Utils的Container.create()等。git

高階組件

其實對於通常的中小項目來講,你只須要用到以上的這三種組件方式就能夠很好地構造出所需的應用了。可是當面對複雜的需求的時候,咱們每每能夠利用高階組件(Higher-Order Component)編寫出可重用性更強的組件。那麼什麼是高階組件呢?其實它和高階函數的概念相似,就是一個會返回組件的組件。或者更確切地說,它實際上是一個會返回組件的函數。就像這樣:github

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

作爲一個高階組件,能夠在原有組件的基礎上,對其增長新的功能和行爲。咱們通常但願編寫的組件儘可能純淨或者說其中的業務邏輯儘可能單一。可是若是各類組件間又須要增長新功能,如打印日誌,獲取數據和校驗數據等和展現無關的邏輯的時候,這些公共的代碼就會被重複寫不少遍。所以,咱們能夠抽象出一個高階組件,用以給基礎的組件增長這些功能,相似於插件的效果。編程

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

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

   getTrigger(trigger, validator) {
      var originTrigger = this.props[trigger];

      return function(event) {
          //觸發驗證機制,更新狀態
          // do something ...
          originTrigger(event);
      }
  }

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

  return <WrappedComponent {...newProps} /> }複製代碼

值得提一句,一樣是給組件增長新功能的方法,相比於使用mixins這種方式高階組件則更加簡潔和職責更加單一。你若是使用過多個mixins的時候,狀態污染就十分容易發生,以及你很難從組件的定義上看出隱含在mixins中的邏輯。而高階組件的處理方式則更加容易維護。後端

另外一方面,ES7中新的語法Decorator也能夠用來實現和上面寫法同樣的效果。

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組件

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

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>);複製代碼

父組件獲取了內部的渲染邏輯,所以在須要控制渲染機制時可使用這種組件形式。

總結

以上這些組件編寫模式基本上能夠覆蓋目前工做中所須要的模式。在寫一些複雜的框架組件的時候,仔細設計和研究組件間的解耦和組合方式,可以使後續的項目可維護性大大加強。

參考文檔

React Patterns - Render Callback
React Higher Order Components in depth

相關文章
相關標籤/搜索