建立 React 元件三種“姿勢”

小武的知識鋪發表於2018-12-12

React 世界中,組成一個頁面的最小單元為一個個元件,很顯然如何合理的建立它們是個非常關鍵的點。下面介紹一下我對三種建立元件方式的理解與總結。

從簡單開始:函式式元件

瞭解過 React 的人都知道,一個 Component 資料來源有兩個:

  1. 來自外部的屬性(props)
  2. 來自內部的狀態(State)

當函式式的建立一個元件之後,顯然它已經註定了沒法再擁有自己的 State 了,只能 “無腦” 的去獲取屬性內容並展示,因為函式式元件其實是一個只實現了 render 函式的元件。

所以把這種元件稱之為 “小傻瓜元件” 非常的形象

建立 React 元件三種“姿勢”

一個 “傻瓜函式式元件” 的例子:

// 這一句匯入必須新增, JSX 語境依賴於此
import React from 'react';

export default ({order="沒人下達命令,我就啥也不幹"}) => {
    return (
        <div>
            我是“傻瓜元件”,我完全服從命令:{order}.
        </div>
    );
};
複製程式碼

這個簡單的元件只接受一個 order 的 ,並且為這個屬性設定了一個預設值。

  1. 當使用這個元件的地方為它設定了 order 屬性,就會列印外部設定的order屬性值;
  2. 如果沒有設定,就會列印這個預設值(ES6 語法允許給定義的函式引數設定一個預設值)

這裡使用了 ES6 箭頭語法,快速的建立了一個無狀態的函式式元件並 exoprt 出去供外界使用。

複雜點的:繼承 React Component 相關類實現

React 版本16以後,官方已經不推薦使用 createclass 的方式建立元件了,同時也取消了以前 Mixin 那一套侵入式複用程式碼的做法,轉而推薦大家使用高階函式的方式來實現對程式碼的複用

繼承的方式建立一個元件可以繼承兩個父類:

  1. React.Component
  2. React.PureComponent

PureComponent 和 Component 除了在 shouldComponentUpdate 方法的實現邏輯上不一樣,其他表現都一樣,PureComponent使用了props和state的淺比較。

一般情況下我們選擇繼承 React.PureComponent 可以優化我們的元件效能,不過特殊時候你也可以選擇繼承 React.Component,然後自己去實現 shouldComponentUpdate 方法的邏輯部分。

相比“傻瓜式”元件,繼承方式獲得的元件更加 “聰明” 了:

1.它可以有自己的屬性 2.它可以有自己的狀態 3.它可以有自己的生命週期邏輯

建立 React 元件三種“姿勢”

一個簡單的 “聰明的” 繼承式的元件例子:

import React from "react";
import PropTypes from "prop-types";

export default class ComponentOne extends React.PureComponent {
  static defaultProps = {
    propOne: "預設屬性"
  };

  static propTypes = {
    propOne: PropTypes.string.isRequired
  };

  constructor(props) {
    super(props);
    console.log("建構函式");
    this.clickToAdd = this.clickToAdd.bind(this);
    this.state = {
      count: 0
    };
  }

  clickToAdd() {
    this.setState({
      count: this.state.count + 1
    });
  }

  componentDidMount() {
    console.log("介面裝載完成");
  }

  render() {
    return (
      <div>
          <div>
          我是由繼承 React.PureComponent 建立的元件。並且我有屬性:{this.props.propOne}
          </div>
          <div>
              當前計數:{this.state.count}
          </div>
          <div>
              <button onClick={this.clickToAdd}>點選計數增加</button>
          </div>
      </div>
    );
  }
}
複製程式碼

需要注意的一點,使用繼承的方式建立的元件,它的成員函式都不會自動繫結 this,推薦在建構函式 constructor 中進行手動 bind。

上面程式碼中的元件有自己的狀態,有自己的屬性,也自己實現了生命週期函式中的 componentDidMount,在裡面簡單列印了一句 log。

更厲害的:高階元件 HOC(Higher Order Component)

高階元件,我的理解就是一個能接受元件為引數,並能返回一個新元件的函式。高階元件有下面兩種實現方式:

1. 代理模式(組合)

先看示例程式碼:

import React from 'react';

const agent = (WrappedComponent, newProps) => {
    return class WrappingComponent extends React.PureComponent{
        render(){
            return <WrappedComponent {...this.props} {...newProps}/>
        }
    }
}

export default agent;
複製程式碼

WrappingComponent 和 WrappedComponent 都分別走一遍自己的生命週期,代理模式下的高階函式是兩個元件的一個組合。

可以用此來操縱傳入的 props,達到複用程式碼的目的。

2. 繼承模式(繼承)

先看示例程式碼:

import React from 'react';

const OnlyForLoginComponent = (WrappedComponent) => {
    return class WrappingComponent extends WrappedComponent{
        render(){
            if (this.props.logined) {
                return super.render();
            } else {
                return (
                    <div>
                        請先登入。
                    </div>
                );
            }
        }
    }
}

export default OnlyForLoginComponent;
複製程式碼

可以看到這裡通過繼承的方式實現了一個新的元件並返回。採用繼承的方式實現的高階元件,是一種合二為一的方式,因此我們可以操縱 WrappedComponent 的生命週期,當然也可以操縱它的屬性。

比如上面的程式碼展示的就是,使用高階元件生成了一個登入狀態判斷的狀態元件,如果登入了就繼續渲染 super(也就是 WrappedComponent),否則就提示“請先登入”。

熟悉 React-Redux 庫的同學可以探索一下它的 connect 函式,這個函式的返回值就是一個高階元件,它接受一個元件並返回一個新元件。

到此,三種主流的建立新元件的方式已經總結完畢,這些內容也是我自己在學習實踐過程中的一些總結,寫下來分享給大家,希望共同進步。


建立 React 元件三種“姿勢”

相關文章