1. 程式人生 > >React 設計模式 --- Container and Presentational pattern(容器和展示組件分離)

React 設計模式 --- Container and Presentational pattern(容器和展示組件分離)

htm con o-c 進行 style ade 不同 後綴 其他

  在React開發中,一個典型的React組件通常會混雜著邏輯操作部分和展示部分。邏輯操作部分指的是和頁面UI無關的內容,如API的調用,數據的處理,事件處理函數。 展示部分則指的是創建頁面UI 的內容,就是組件中render 函數的內容。

  簡單地寫一個組件Geo 來看一下,這個組件會展示我們的位置信息。為了簡單起見,用create-react-app創建項目。項目中的src目錄主要存放源代碼,所以我們在其內部新建一個目錄components, 用於存放我們的組件。一般我們直接寫js 文件,暴露出我們的組件,供其他組件使用。在components 目錄下新建Geo.js 文件,代碼如下,很簡單,由於展示經緯度,所以需要兩個狀態: lon/lat, 分別表示經度/緯度;同時,組件渲染完成後,在其生命周期函數componentDidMount 下調用html5 的navigator Api 獲取經緯度。

import React, {Component} from ‘react‘;

export default class  Geo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      lat: null,   // 緯度
      lon: null    // 經度
    }

    this.handleSucess = this.handleSucess.bind(this)
  }
  //調用navigator API 獲取當前的位置
  componentDidMount() {
    
if(navigator.geolocation) { navigator.geolocation.getCurrentPosition(this.handleSucess) } }   // 位置獲取成功後的回調函數 handleSucess({coords}) { var lat = coords.latitude.toFixed(2); var lon = coords.longitude.toFixed(2); this.setState({ lat , lon }) } render() {
return ( <div> <div>緯度: {this.state.lat}</div> <div>經度: {this.state.lon} </div> </div> ) } }

  然後在App.js 中 引入Geo組件,就是在App.js 文件上部寫下

import Geo from ‘./components/Geo‘;

  並將App render 函數中以下代碼

<p className="App-intro">
      To get started, edit <code>src/App.js</code> and save to reload.
 </p>

  改為

<div className="App-intro">
      <Geo />
</div>

  這時頁面可以看到經緯度的展示,在chrome 需要FQ,因為他調用的是google地圖,你可以用手機看一下效果

技術分享

  很明顯地可以看到邏輯和展示的混雜。Navigator API 就是邏輯操作,render函數則是純展示部分。

  這時我們看一下容器和展示模式。容器和展示模式指的是把組件的邏輯操作部分和展示部分進行分離,分別放到不同的文件中,這樣,每一個組件都會分成兩個部分,兩者都有各自的職責,一個只負責操作邏輯,叫做容器container, 一個只負責頁面展示,叫做展示Presentational。 現在把我們的Geo 組件按照容器和展示模式進行一下拆分,這時新建一下js 文件,命名為Geo-container.js,它是一個容器組件,書寫頁邏輯,那麽原來的Geo.js文件就變成了純渲染組件。這種命名規則是React社區的規範,加container 後綴表示容器組件,不加則表示展示組件。

  Geo-container.js 內容如下,它是直接把展示組件引入,然後對其進行傳參

import React, {Component} from ‘react‘;
import Geo from ‘./Geo‘                 // 引進展示組件

export default class  GeoContainer extends Component {
  constructor(props) {
    super(props);
    this.state = {
      lat: null,   // 緯度
      lon: null    // 經度
    }

    this.handleSucess = this.handleSucess.bind(this)
  }

  componentDidMount() {
    if(navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(this.handleSucess)
    }
  }

  handleSucess({coords}) {
    var lat = coords.latitude.toFixed(2);
    var lon = coords.longitude.toFixed(2);
    this.setState({
      lat ,
      lon
    })
  }
  // 在容器組件內部,只渲染我們引入的展示組件,並把狀態當做參數進行傳遞
  render() {
    return (<Geo {...this.state}/>)
  }
}

  Geo.js 由於變成了純渲染組件,所以用無狀態組件的樣式進行書寫,

import React from ‘react‘;

// 由於展示組件內部沒有任何的狀態,只負責展示,所以用無狀態組件。
const Geo = ({lat,lon})  => {
    return (
      <div>
        <div>緯度: {lat}</div>
        <div>經度: {lon} </div>
      </div>
    )
  }

export default Geo

  在App.js中直接引入容器組件

import React, { Component } from ‘react‘;
import logo from ‘./logo.svg‘;
import ‘./App.css‘;

import GeoContainer from ‘./components/GeoContainer‘;    // 引入我們的空器組件

class App extends Component {
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        {/*渲染我們的容器組件*/}
        <div className="App-intro">
          <GeoContainer />
        </div>
      </div>
    );
  }
}

export default App;

  這樣同樣實現了我們的功能,但職責更為了清晰,組件更容易被復用

React 設計模式 --- Container and Presentational pattern(容器和展示組件分離)