React 設計模式 --- Container and Presentational pattern(容器和展示組件分離)
在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(容器和展示組件分離)