React組件 純組件 函數(shù)組件 高階組件

一、組件

(1) 函數(shù)組件

如果你想寫的組件只包含一個 render 方法,并且不包含 state,那么使用函數(shù)組件就會更簡單。我們不需要定義一個繼承于 React.Component 的類,我們可以定義一個函數(shù),這個函數(shù)接收 props 作為參數(shù),然后返回需要渲染的元素。

function Square(props) {
  return (
    <button className="square" onClick={props.onClick}>
    {props.value}
    </button>
  );
}

(2) React.Component

shouldComponentUpdate 僅檢查了 props.color 或 state.count 是否改變。如果這些值沒有改變,那么這個組件不會更新

class CounterButton extends React.Component {

  shouldComponentUpdate(nextProps, nextState) {
    if (this.props.color !== nextProps.color) {
      return true;
    }
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }
}

(3) PureComponent

如果你的組件更復(fù)雜一些,你可以使用類似“淺比較”的模式來檢查 props 和 state 中所有的字段,以此來決定是否組件需要更新。React 已經(jīng)提供了一位好幫手來幫你實(shí)現(xiàn)這種常見的模式 - 你只要繼承 React.PureComponent 就行了。

class CounterButton extends React.PureComponent {}

大部分情況下,你可以使用 React.PureComponent 來代替手寫 shouldComponentUpdate。但它只進(jìn)行淺比較 (例如:1 == 1或者ture==true,數(shù)組和對象引用是否相同),所以當(dāng) props 或者 state 某種程度是可變的話,淺比較會有遺漏,那你就不能使用它了。

不要在props和state中改變對象和數(shù)組,如果你在你的父組件中改變對象,你的PureComponent將不會更新。雖然值已經(jīng)被改變,但是子組件比較的是之前props的引用是否相同,所以不會檢測到不同。

因此,你可以通過使用es6的assign方法或者數(shù)組的擴(kuò)展運(yùn)算符或者使用第三方庫,強(qiáng)制返回一個新的對象。

當(dāng)數(shù)據(jù)結(jié)構(gòu)很復(fù)雜時(shí),情況會變得麻煩,存在性能問題。
(比較原始值和對象引用是低耗時(shí)操作。如果你有一列子對象并且其中一個子對象更新,對它們的props和state進(jìn)行檢查要比重新渲染每一個子節(jié)點(diǎn)要快的多。)

(4) 何時(shí)使用Component 或 PureComponent ?

<1> 當(dāng)組件是獨(dú)立的,組件在頁面中的個數(shù)為1或2的,組件有很多props、state,并且當(dāng)中還有些是數(shù)組和對象的,組件需要每次都渲染的,使用Component

<2> 當(dāng)組件經(jīng)常作為子組件,作為列表,組件在頁面中數(shù)量眾多,組件props, state屬性少,并且屬性中基本沒有數(shù)組和對象,組件不需要每次都渲染,只有變化了才渲染,使用PureComponent

憑主觀,我覺得
以下組件適合Component

Button
Input

以下組件適合PureComponent

Radio
Checkbox
Option

二、高階函數(shù)

HOC ( 高階組件higherOrderComponent ) 自身不是 React API 的一部分,它是一種基于 React 的組合特性而形成的設(shè)計(jì)模式。

組件是將 props 轉(zhuǎn)換為 UI,而高階組件是將組件轉(zhuǎn)換為另一個組件。(組件是 React 中代碼復(fù)用的基本單元。)

高階組件例如 Redux 的 connect 和 Relay 的 createFragmentContainer。

(1)HOC 不會修改傳入的組件,也不會使用繼承來復(fù)制其行為。

相反,HOC 通過將組件包裝在容器組件中來組成新組件。HOC 是純函數(shù),沒有副作用。

(2)HOC 應(yīng)該透傳與自身無關(guān)的 props

HOC 為組件添加特性。自身不應(yīng)該大幅改變約定。
HOC 應(yīng)該透傳與自身無關(guān)的 props,HOC 返回的組件與原組件應(yīng)保持類似的接口。

(3)約定:包裝顯示名稱以便輕松調(diào)試HOC

創(chuàng)建的容器組件會與任何其他組件一樣,會顯示在 React Developer Tools 中。為了方便調(diào)試,請選擇一個顯示名稱,以表明它是 HOC 的產(chǎn)物。

最常見的方式是用 HOC 包住被包裝組件的顯示名稱。比如高階組件名為 withSubscription,并且被包裝組件的顯示名稱為 CommentList,顯示名稱應(yīng)該為 WithSubscription(CommentList):

function withSubscription(WrappedComponent) {
  class WithSubscription extends React.Component {/* ... */}
  WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`;
  return WithSubscription;
}

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}

(4) 注意事項(xiàng):

<1> 不要在 render 方法中使用 HOC

render() {
  // 每次調(diào)用 render 函數(shù)都會創(chuàng)建一個新的 EnhancedComponent
  // EnhancedComponent1 !== EnhancedComponent2

  const EnhancedComponent = enhance(MyComponent);

  // 這將導(dǎo)致子樹每次渲染都會進(jìn)行卸載,和重新掛載的操作!

  return <EnhancedComponent />;
}

<2>務(wù)必復(fù)制靜態(tài)方法

有時(shí)在 React 組件上定義靜態(tài)方法很有用。例如,Relay 容器暴露了一個靜態(tài)方法 getFragment 以方便組合 GraphQL 片段。

但是,當(dāng)你將 HOC 應(yīng)用于組件時(shí),原始組件將使用容器組件進(jìn)行包裝。這意味著新組件沒有原始組件的任何靜態(tài)方法。

// 定義靜態(tài)函數(shù)
WrappedComponent.staticMethod = function() {/*...*/}
// 現(xiàn)在使用 HOC
const EnhancedComponent = enhance(WrappedComponent);

// 增強(qiáng)組件沒有 staticMethod
typeof EnhancedComponent.staticMethod === 'undefined' // true
為了解決這個問題,你可以在返回之前把這些方法拷貝到容器組件上:

你可以使用 hoist-non-react-statics 自動拷貝所有非 React 靜態(tài)方法:

import hoistNonReactStatic from 'hoist-non-react-statics';
function enhance(WrappedComponent) {
  class Enhance extends React.Component {/*...*/}
  hoistNonReactStatic(Enhance, WrappedComponent);
  return Enhance;
}

除了導(dǎo)出組件,另一個可行的方案是再額外導(dǎo)出這個靜態(tài)方法。

// 使用這種方式代替...
MyComponent.someFunction = someFunction;
export default MyComponent;

// ...單獨(dú)導(dǎo)出該方法...
export { someFunction };

// ...并在要使用的組件中,import 它們
import MyComponent, { someFunction } from './MyComponent.js';

<3> Refs 不會被傳遞

雖然高階組件的約定是將所有 props 傳遞給被包裝組件,但這對于 refs 并不適用。那是因?yàn)?ref 實(shí)際上并不是一個 prop - 就像 key 一樣,它是由 React 專門處理的。如果將 ref 添加到 HOC 的返回組件中,則 ref 引用指向容器組件,而不是被包裝組件。

這個問題的解決方案是通過使用 React.forwardRef API(React 16.3 中引入)

三、React Redux 的 connect

React Redux 的 connect 函數(shù)是一個 返回高階組件的高階函數(shù)!

最常見的 HOC 簽名如下:

// React Redux 的 `connect` 函數(shù)
const ConnectedComment = connect(commentSelector, commentActions)(CommentList);

剛剛發(fā)生了什么?!如果你把它分開,就會更容易看出發(fā)生了什么。

// connect 是一個函數(shù),它的返回值為另外一個函數(shù)。
const enhance = connect(commentListSelector, commentListActions);

// 返回值為 HOC,它會返回已經(jīng)連接 Redux store 的組件
const ConnectedComment = enhance(CommentList);

這種形式可能看起來令人困惑或不必要,但它有一個有用的屬性。最大化可組合性。
像 connect 函數(shù)返回的單參數(shù) HOC 具有簽名 Component => Component。 輸出類型與輸入類型相同的函數(shù)很容易組合在一起。

// 而不是這樣...
const EnhancedComponent = withRouter(connect(commentSelector)(WrappedComponent))

// ... 你可以編寫組合工具函數(shù)
// compose(f, g, h) 等同于 (...args) => f(g(h(...args)))

const enhance = compose(
  // 這些都是單參數(shù)的 HOC
  withRouter,
  connect(commentSelector)
)
const EnhancedComponent = enhance(WrappedComponent)

//(同樣的屬性也允許 connect 和其他 HOC 承擔(dān)裝飾器的角色)

四、其他

(1)key

每當(dāng)一個列表重新渲染時(shí),React 會根據(jù)每一項(xiàng)列表元素的 key 來檢索上一次渲染時(shí)與每個 key 所匹配的列表項(xiàng)。如果 React 發(fā)現(xiàn)當(dāng)前的列表有一個之前不存在的 key,那么就會創(chuàng)建出一個新的組件。如果 React 發(fā)現(xiàn)和之前對比少了一個 key,那么就會銷毀之前對應(yīng)的組件。如果一個組件的 key 發(fā)生了變化,這個組件會被銷毀,然后使用新的 state 重新創(chuàng)建一份。

我們強(qiáng)烈推薦,每次只要你構(gòu)建動態(tài)列表的時(shí)候,都要指定一個合適的 key。

如果你沒有指定任何 key,React 會發(fā)出警告,并且會把數(shù)組的索引當(dāng)作默認(rèn)的 key。但是如果想要對列表進(jìn)行重新排序、新增、刪除操作時(shí),把數(shù)組索引作為 key 是有問題的

顯式地使用 key={i} 來指定 key 確實(shí)會消除警告,但是仍然和數(shù)組索引存在同樣的問題,所以大多數(shù)情況下最好不要這么做。

組件的 key 值并不需要在全局都保證唯一,只需要在當(dāng)前的同一級元素之前保證唯一即可。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 深入JSX date:20170412筆記原文其實(shí)JSX是React.createElement(componen...
    gaoer1938閱讀 8,175評論 2 35
  • 高階組件是react應(yīng)用中很重要的一部分,最大的特點(diǎn)就是重用組件邏輯。它并不是由React API定義出來的功能,...
    叫我蘇軾好嗎閱讀 974評論 0 0
  • React進(jìn)階之高階組件 前言 本文代碼淺顯易懂,思想深入實(shí)用。此屬于react進(jìn)階用法,如果你還不了解react...
    流動碼文閱讀 1,227評論 0 1
  • 3. JSX JSX是對JavaScript語言的一個擴(kuò)展語法, 用于生產(chǎn)React“元素”,建議在描述UI的時(shí)候...
    pixels閱讀 2,971評論 0 24
  • 今天的React題沒有太多的故事…… 半個月前出了248個Vue的知識點(diǎn),受到很多朋友的關(guān)注,都強(qiáng)烈要求再出多些R...
    浪子神劍閱讀 10,223評論 6 106

友情鏈接更多精彩內(nèi)容