react組件的幾個模式

React Component Patterns

有狀態組件 x 無狀態組件、容器組件 x 展現組件、高階組件 x Render Callbacks(Function as Child Component)react

使用React已經有一段時間了, React——Facebook庫,使用JS構建用戶界面。本文試圖總結迄今爲止實踐中所學到的一些模式,同時但願可以幫助到即將邁入奇妙地組件世界的開發者。git

react-pattern-post.png

有狀態組件 X 無狀態組件

正如web服務有靜態和動態之分,React組件也有有狀態和無狀態的區分。有狀態組件--在應用中組件能夠擁有自身狀態並操縱它;無狀態組件--只接收屬性進行效果呈現。web

一個簡單的無狀態組件,只受屬性控制:編程

const Button = props => (
  <button onClick={props.onClick}>
    {props.text}
  </button>
);

一個具備計數功能的按鈕組件(複用上面Button組件)小程序

class ButtonCounter extends React.Component {
  constructor() {
    super()
    this.state = { clicks: 0 }
    this.handleClick = this.handleClick.bind(this)
  }

  handleClick() {
    this.setState({ clicks: this.state.clicks + 1 })
  }

  render() {
    return (
      <Button
        onClick={this.handleClick}
        text={`You've clicked me ${this.state.clicks} times!`}
      />
    )
  }
}

正如上面兩個 Demo 所示,第二個組件的 constructor 具備狀態的定義,第一個組件只是單純地渲染屬性文字。有狀態組件和無狀態組件的劃分看起來很是簡單,可是它對於組件複用具備重大意義。瀏覽器

容器組件 X 展現組件

當組件須要獲取外部數據時,咱們又能夠將組件劃分爲兩種新的類型。容器組件負責獲取數據,它經常是超出了React範疇的,如使用 Redux 或 Relay 進行了綁定。對比而言,展現型組件不依賴於程序其餘部分,它只和自身狀態或屬性有關。下面咱們實現一個用戶列表的展現組件:編程語言

const UserList = props =>
  <ul>
    {props.users.map(u => (
      <li>{u.name} — {u.age} years old</li>
    ))}
  </ul>

容器組件能夠用來更新用戶列表的展現:函數

class UserListContainer extends React.Component {
  constructor() {
    super()
    this.state = { users: [] }
  }

  componentDidMount() {
    fetchUsers(users => this.setState({ users }))
  }

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

這種分類將數據獲取和渲染的邏輯分開,進而使用戶列表組件能夠複用。post

若是你想了解更多該模式的信息,awesome article from Dan Abramov 對它進行了精確的解釋。學習

高階組件 -- HOCs

當你想複用組件邏輯時,高階組件很是有用。高階組件--是將組件做爲參數並返回新組件的 JS 函數

假設你須要構建一個可擴展菜單組件,當用戶點擊時,它會顯示隱藏子組件內容。所以,你可使用高階組件來實現:

function makeToggleable(Clickable) {
  return class extends React.Component {
    constructor() {
      super()
      this.toggle = this.toggle.bind(this)
      this.state = { show: false }
    }

    toggle() {
      this.setState(prevState => ({ show: !prevState.show }))
    }

    render() {
      return (
        <div>
          <Clickable
            {...this.props}
            onClick={this.toggle}
          />
          {this.state.show && this.props.children}
        </div>
      )
    }
  }
}

這種方法容許咱們使用 ES7裝飾器語法將邏輯應用於 ToggleableMenu 組件:

@makeToggleable
class ToggleableMenu extends React.Component {
  render() {
    return (
      <div onClick={this.props.onClick}>
        <h1>{this.props.title}</h1>
      </div>
    )
  }
}

如今,咱們能夠將任何子組件傳遞給ToggleableMenu組件:

class Menu extends React.Component {
  render() {
    return (
      <div>
        <ToggleableMenu title="First Menu">
          <p>Some content</p>
        </ToggleableMenu>
        <ToggleableMenu title="Second Menu">
          <p>Another content</p>
        </ToggleableMenu>
        <ToggleableMenu title="Third Menu">
          <p>More content</p>
        </ToggleableMenu>
      </div>
    )
  }
}

若是你熟悉 Redux 的 connect函數或者 React Router 的withRouter函數,那麼你已經使用太高階組件了。

渲染回調 -- Render Callbacks(Function as Child Components)

另外一個比較高端的複用組件邏輯的方法是將函數做爲組件的 props.children,該方法也稱爲Function as Child Components。咱們將使用渲染回調來從新實現上面的可擴展Menu:

class Toggleable extends React.Component {
  constructor() {
    super()
    this.toggle = this.toggle.bind(this)
    this.state = { show: false }
  }

  toggle() {
    this.setState(prevState => ({ show: !prevState.show }))
  }

  render() {
    return this.props.children(this.state.show, this.toggle)
  }
}

如今,咱們能夠將函數做爲組件的子級進行傳遞:

<Toggleable>
    {(show, onClick) => (
      <div>
        <div onClick={onClick}>
          <h1>{props.title}</h1>
        </div>
        {show && props.children}
      </div>
    )}
  </Toggleable>

上面的代碼已經將一個函數做爲子組件,可是,若咱們想複用上述邏輯,咱們須要建立一個轉換邏輯的新組件:

const ToggleableMenu = props =>
  <Toggleable>
    {(show, onClick) => (
      <div>
        <div onClick={onClick}>
          <h1>{props.title}</h1>
        </div>
        {show && props.children}
      </div>
    )}
  </Toggleable>

咱們使用Render Callbacks實現的可擴展的Menu組件以下:

class Menu extends React.Component {
  render() {
    return (
      <div>
        <ToggleableMenu title="First Menu">
          <p>Some content</p>
        </ToggleableMenu>
        <ToggleableMenu title="Second Menu">
          <p>Another content</p>
        </ToggleableMenu>
        <ToggleableMenu title="Third Menu">
          <p>More content</p>
        </ToggleableMenu>
      </div>
    )
  }
}

Render Callbacks和高階函數使咱們的組件更加靈活,掌握和適應起來具備必定的難度,須要反覆學習和消化。


本人能力有限,若有紕漏,請指正。

【開發環境推薦】 Cloud Studio 是基於瀏覽器的集成式開發環境,支持絕大部分編程語言,包括 HTML五、PHP、Python、Java、Ruby、C/C++、.NET 小程序等等,無需下載安裝程序,一鍵切換開發環境。 Cloud Studio提供了完整的 Linux 環境,而且支持自定義域名指向,動態計算資源調整,能夠完成各類應用的開發編譯與部署。
相關文章
相關標籤/搜索