REACT元件抽象與複用

en1475230123000發表於2018-11-22

當我們在說元件抽象與複用的時候我們在說什麼?本質就是元件的拆分。。

為什麼拆

“分而治之”是一個好策略。但是不要濫用,只有必要的時候才去拆分元件, 不然可能得不償失 。

拆分原則

高內聚低耦合 。

高內聚:就是把邏輯緊密相關的內容放在一個元件中。這段js,css,html都是為了實現同一個功能我們把它放在一個js檔案裡。react天生具有高內聚的特點。

低耦合:指的是不同元件之間的依賴關係要儘量弱化,也就是每個元件要儘量獨立 。 保持整個系統的低耦合度,需要對系統中的功能有充分的認識,然後根據功能點劃分模 塊,讓不同的元件去實現不同的功能

科普元件分類

函式元件(Functional Component )和類元件(Class Component)

劃分依據是根據元件的定義方式。

// 函式元件
function Hello(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 類元件
class Hello extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
複製程式碼

類元件可以維護自身的狀態變數,即元件的state,類元件還有不同的生命週期方法,可以讓開發者能夠在元件的不同階段(掛載、更新、解除安裝),對元件做更多的控制。 類元件這麼強大,首選類元件? 不,函式元件更加專注和單一,承擔的職責也更加清晰,它只是一個返回React 元素的函式,只關注對應UI的展現。函式元件接收外部傳入的props,返回對應UI的DOM描述,僅此而已。 函式元件的使用可以從思想上迫使你在設計元件時多做思考,更加關注邏輯和顯示的分離,設計出更加合理的頁面上元件樹的結構。實際操作上,當一個元件不需要管理自身狀態時,可以把它設計成函式元件,當你有足夠的理由發現它需要“升級”為類元件時,再把它改造為類元件。

無狀態元件(Stateless Component )和有狀態元件(Stateful Component)

劃分依據是根據元件內部是否維護state。無狀態元件內部不使用state,只根據外部元件傳入的props返回待渲染的React 元素。有狀態元件內部使用state,維護自身狀態的變化,有狀態元件根據外部元件傳入的props和自身的state,共同決定最終返回的React 元素。函式元件一定是無狀態元件,類元件則既可以充當無狀態元件,也可以充當有狀態元件

展示型元件(Presentational Component)和容器型元件(Container Component)

劃分依據是根據元件的職責.

展示型元件的職責是:元件UI長成什麼樣。展示型元件不關心元件使用的資料是如何獲取的,以及元件資料應該如何修改,它只需要知道有了這些資料後,元件UI是什麼樣子的即可。外部元件通過props傳遞給展示型元件所需的資料和修改這些資料的回撥函式,展示型元件只是它們的使用者。展示型元件一般是無狀態元件,不需要state,因為展示型元件不需要管理資料,但當展示型元件需要管理自身的UI狀態時,例如控制元件內部彈框的顯示與隱藏,是可以使用state的,這時的state屬於UI state。既然大部分情況下展示型元件不需要state,應該優先考慮使用函式元件實現展示型元件。

容器型元件的職責是:元件資料如何工作。容器型元件需要知道如何獲取子元件所需資料,以及這些資料的處理邏輯,並把資料和邏輯通過props提供給子元件使用。容器型元件一般是有狀態元件,因為它們需要管理頁面所需資料。

class UserListContainer extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      users: []
    }
  }
  
  componentDidMount() {
    var that = this;
    fetch('/path/to/user-api').then(function(response) {
      response.json().then(function(data) {
        that.setState({users: data})
      });
    });
  }

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

function UserList(props) {
  return (
    <div>
      <ul className="user-list">
        {props.users.map(function(user) {
          return (
            <li key={user.id}>
              <span>{user.name}</span>
            </li>
          );
        })}
      </ul>
    </div>
  )  
}
複製程式碼

這三組概念都體現了關注點分離的思想:UI展現和資料邏輯的分離。函式元件、無狀態元件和展示型元件主要關注UI展現,類元件、有狀態元件和容器型元件主要關注資料邏輯。

它們之間的關聯關係可以歸納為:函式元件一定是無狀態元件,展示型元件一般是無狀態元件;類元件既可以是有狀態元件,又可以是無狀態元件,容器型元件一般是有狀態元件。

tips:受控元件與非受控元件,業務元件,ui元件,

實現

高階元件( Higher Order Component, HOC)

並不是 React提供的某種 API,而是使 用 React 的一種模式,用於增強現有元件的功能 簡單來說,一個高階元件就是一個函式,這個函式接受一個元件作為輸入,然後返回一個新的元件作為結果,而且,返回的新元件擁有了輸入元件所不具有的功能 。 這裡提到的元件指的並不是元件例項,而是一個元件類,也可以是一個無狀態元件 的函式

import React from ’ react ’ J
function removeUserProp(WrappedComponent) {
  return class WrappingComponent extends React.Component {
    render() {
      const {user, ... otherProps} = this.props; 
      return <WrappedComponent {... otherProps) />
     }
}
export default removeUserProp;
複製程式碼

分類

根據返回的新元件和傳人元件引數的關係,高階元件的實現方式可以分為兩大類:

代理方式的 高階元件 :

上面的 removeUserProp例子就是一個代理方式的高階元件,特點是返回的新元件類 直接繼承自 React.Component類。 新元件扮演的角色是傳入引數元件的一個“代理”,在 新組建的 render 函式中,把被包裹元件渲染出來,除了高階元件自己要做的工作,其餘 功能全都轉手給了被包裹的元件 。

function removeUserProp(WrappedComponent) { 
return function newRender(props) {
        const {user, ... otherProps) = props;
     return <WrappedComponent {... otherProps} />
  }
}
複製程式碼

應用在下列場景中:

操縱 prop;

訪問 ref:(訪問 ref並不是值得推薦的 React元件使用方式)

抽取狀態: 其實,我們已經使用過“抽取狀態”的高階元件了,就是 react-redux 的 connect 函 數,注意 connect 函式本身並不是高階元件, connect 函式執行的結果是另 一個函式,這 個函式才是高階元件 。

包裝元件 。

const styleHOC = (WrappedComponent, style) => {
return class HOCComponent extends React.Component {
    render() { 
    return (
        <div style={style}>
         <WrappedComponent {... this.props}/>
        </div>
    }
 }
}
複製程式碼

繼承方式的高階元件 。

class PureComponent extends Component{
    shouldComponentUpdate(nextProps,nextState){
     const {props,state} = this;
     return shallowCompare(nextProps,props) && shallowCompare(nextState,state)
        
    }
}
複製程式碼
const modi fyProps HOC = (WrappedComponent) => {
    return class NewComponent extends WrappedComponent {
        render() {
        const elements= super.render() ;
        const newStyle = {
          color: (elements && elements.type ===’div’)?’red’:’green’
        }
        const newProps = { ... this.props, style: newStyle};
         return React.cloneElement(elements, newProps,elements.props.children);
        }
    }
}
複製程式碼

代理方式更加容易實現和控制,繼承方式的唯一優勢是可以操縱特定元件的生命週期函式。 “優先考慮組合,然後才考慮繼承 。”

以函式為子元件

const loggedinUser = ’ mock user ’;
class AddUserProp extends React.Component {
    render() {
        const user = loggedinUser ;
        return this.props.children(user);
    }
}
AddUserProp.propTypes = {
 children: React.PropTypes.func.isRequired
}
複製程式碼

這個類的程式碼,和被增強元件的唯一聯絡就是 this.props.children,而且 this.props. children是函式型別,在 render 函式中 直接呼叫 this.props.children 函式 ,引數就是我們 希望傳遞下去的 user。 使用這個 AddUserProp 的靈活之處在於它沒有對被增強元件有任何 props 要求,只 是傳遞一個引數過去,至於怎麼使用,完全由作為子元件的函式決定

裝飾器

其他

mixin

extands

相關文章