React的render-props模式、高階組件

render-props模式

將可以復(fù)用的state和操作state的方法封裝到一個(gè)獨(dú)立的組件中,可以提高開(kāi)發(fā)效率

props.render()模式

demo: 定義一個(gè)獲取鼠標(biāo)位置的組件

  • 創(chuàng)建Mouse組件,在組件中提供復(fù)用的代碼邏輯

  • 將狀態(tài)作為props.render(state)方法的參數(shù),暴露到組件外部

    注意: 不一定要叫render也可以使用其他名稱,復(fù)用時(shí)與之一致即可

import { Component } from 'react'

export default class Mouse extends Component {
  state = {
    x: 0,
    y: 0
  }
  // 監(jiān)聽(tīng)鼠標(biāo)移動(dòng)事件
  componentDidMount() {
    window.addEventListener('mousemove', this.handleMouseMove)
  }
  // 組件限載時(shí)解綁事件
  componentWillUnmount() {
    window.removeEventListener('mousemove', this.handleMouseMove)
  }
  handleMouseMove = e => {
    this.setState({
      x: e.clientX,
      y: e.clientY
    })
  }
  render() {
    // 向外界提供當(dāng)前子組件里面的數(shù)據(jù) render模式
    // show是自定義的名稱,復(fù)用時(shí)與之對(duì)應(yīng)即可
    return this.props.show(this.state)
  }
}

定義一個(gè)Position類式組件

import React, { Component } from 'react'

export default class Position extends Component {
  render() {
    return (
      <div>
        <h2>鼠標(biāo)當(dāng)前x坐標(biāo)軸: {this.props.x}</h2>
        <h2>鼠標(biāo)當(dāng)前y坐標(biāo)軸: {this.props.y}</h2>
      </div>
    )
  }
}

Mouse組件和Position組件引入到App組件中

Mouse組件暴露的方法作為Mouse標(biāo)簽屬性,綁定一個(gè)回調(diào)函數(shù),回調(diào)函數(shù)的形參就是暴露出來(lái)的數(shù)據(jù)

將數(shù)據(jù)傳入需要復(fù)用的組件內(nèi)部

import React, { Component } from 'react'
import Mouse from './Mouse'
import Position from './components/Position'

export default class App extends Component {
  render() {
    return (
      <div>
        {/* render 模式 復(fù)用 類式組件 */}
        
        <Mouse show={(mouse) => <Position {...mouse}></Position>}>
        </Mouse> 
       
      </div>
    )
  }
}

定義一個(gè)Picture函數(shù)式組件,實(shí)現(xiàn)鼠標(biāo)跟隨效果

import React from 'react'
import pic from '../static/1.gif'

export default function Picture(props) {
  return (
    <div>
      <img
        src={pic}
        alt=""
        style={{ position: 'absolute', left: props.x - 100 + 'px', top: props.y - 50 + 'px', width: '200px', height: '200px' }}
      />
    </div>
  )
}

在App組件中引入Picture組件

函數(shù)式組件在使用的時(shí)候,不需要以標(biāo)簽的形式渲染,而是使用函數(shù)的形式,

數(shù)據(jù)直接通過(guò)函數(shù)的參數(shù)傳入組件內(nèi)部

import React, { Component } from 'react'
import Mouse from './Mouse'
import Position from './components/Position'
import Picture from './components/Picture'

export default class App extends Component {
  render() {
    return (
      <div>
        {/* render 模式 復(fù)用 類式組件 */}
        
          <Mouse show={(mouse) => <Position {...mouse}></Position>}>
          </Mouse> 
        
        {/* render 模式 復(fù)用 函數(shù)式組件 */}
        <Mouse show={mouse => Picture(mouse)}></Mouse> 

      </div>
    )
  }
}

使用children代替render

使用props.children()暴露狀態(tài),在復(fù)用組件的時(shí)候,可以將組件當(dāng)成子組件使用

推薦使用children代替render

demo:修改Mouse組件暴露狀態(tài)的方式

import { Component } from 'react'

export default class Mouse extends Component {
  state = {
    x: 0,
    y: 0
  }

  // 監(jiān)聽(tīng)鼠標(biāo)移動(dòng)事件
  componentDidMount() {
    window.addEventListener('mousemove', this.handleMouseMove)
  }
  // 組件限載時(shí)解綁事件
  componentWillUnmount() {
    window.removeEventListener('mousemove', this.handleMouseMove)
  }
  handleMouseMove = e => {
    this.setState({
      x: e.clientX,
      y: e.clientY
    })
  }
  render() {
    // 向外界提供當(dāng)前子組件里面的數(shù)據(jù) children模式
    return this.props.children(this.state)
  }
}

將定義的類式組件和函數(shù)式組件引入到App組件

import React, { Component } from 'react'
import Mouse from './Mouse'
import Position from './components/Position'
import Picture from './components/Picture'

export default class App extends Component {
  render() {
    return (
      <div>
        {/* children 模式 復(fù)用 類式組件 */}
        <Mouse>
          {
            mouse => <Position {...mouse}></Position>
          }
        </Mouse>

        {/* children 模式 復(fù)用 函數(shù)式組件 */}
        <Mouse>
          {
            mouse => Picture(mouse)
          }
        </Mouse>
      </div>
    )
  }
}

高階組件

采用包裝模式來(lái)實(shí)現(xiàn)狀態(tài)的復(fù)用,通過(guò)包裝組件,來(lái)增強(qiáng)組件的功能

使用步驟

  • 在可復(fù)用組件內(nèi)創(chuàng)建一個(gè)函數(shù),函數(shù)名以with開(kāi)頭
  • 指定函數(shù)的參數(shù),參數(shù)名稱以大寫(xiě)字母開(kāi)頭(調(diào)用函數(shù)時(shí)傳入的是組件名稱)
  • 在函數(shù)創(chuàng)建一個(gè)類式組件,提供復(fù)用狀態(tài)的邏輯代碼,并返回
  • 在組件的render()方法里返回以函數(shù)參數(shù)為名稱的標(biāo)簽,并將狀態(tài)傳入組件
  • 引入高階組件,傳入需要增強(qiáng)的組件,并暴露出來(lái)

demo

定義一個(gè)高階組件

import { Component } from 'react'

// 定義一個(gè)函數(shù),在函數(shù)內(nèi)部創(chuàng)建一個(gè)類式組件
// 傳遞的參數(shù)是一個(gè)組件,所以形參的首字母需要大寫(xiě)
const withMouse = WarppedComponent => {
  class Mouse extends Component {
    state = {
      x: 0,
      y: 0
    }
    // 監(jiān)聽(tīng)鼠標(biāo)移動(dòng)事件
    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove)
    }
    // 組件限載時(shí)解綁事件
    componentWillUnmount() {
      window.removeEventListener('mousemove', this.handleMouseMove)
    }
    // 事件的處理函數(shù)
    handleMouseMove = e => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }
    render() {
      // 返回傳遞過(guò)來(lái)的組件思劳,把當(dāng)前組件的狀態(tài)設(shè)置進(jìn)去
      return <WarppedComponent {...this.state} />
    }
  }
  return Mouse
}

export default withMouse

在組件內(nèi)使用高階組件
引入高階組件,將需要增強(qiáng)的組件傳入高階組件

import React, { Component } from 'react'
import withMouse from '../withMouse'

class Position extends Component {
  render() {
    return (
      <div>
        <h2>鼠標(biāo)當(dāng)前x坐標(biāo)軸: {this.props.x}</h2>
        <h2>鼠標(biāo)當(dāng)前y坐標(biāo)軸: {this.props.y}</h2>
      </div>
    )
  }
}

export default withMouse(Position)

在App組件內(nèi)使用的方法和一般情況一致

import React, { Component } from 'react'
import Position from './components/Position'
import Picture from './components/Picture'

export default class App extends Component {
  render() {
    return (
      <div>
        <Position />
        <Picture />
      </div>
    )
  }
}

使用displayName

使用了高階組件之后會(huì)存在一個(gè)問(wèn)題: 開(kāi)發(fā)者工具會(huì)顯示兩個(gè)組件名稱一樣的組件

因?yàn)槟J(rèn)情況下, React會(huì)使用組件名稱作為組件名稱,所有在兩個(gè)組件都使用了高階組件的情況下,兩個(gè)組件的外殼都是其高階組件的名稱

解決方案: 手動(dòng)設(shè)置displayName
使用步驟

  • 在高階組件的函數(shù)內(nèi)部定義一個(gè)函數(shù)getDisplayName(){}把傳入組件作為參數(shù)傳入這個(gè)函數(shù)

    import { Component } from 'react'
    
    // 定義一個(gè)函數(shù),在函數(shù)內(nèi)部創(chuàng)建一個(gè)類式組件
    const withMouse = WarppedComponent => {
      class Mouse extends Component {
        // 復(fù)用的邏輯代碼
      }
      // 設(shè)置開(kāi)發(fā)者工具顯示名稱,如果不設(shè)置開(kāi)發(fā)者工具顯示的外殼組件都是一樣的
      function getDisplayName(WarppedComponent) {
        return WarppedComponent.displayName || WarppedComponent.name || 'Component'
      }
      // 給組件添加displayName屬性
      Mouse.displayName = `WithMouse${getDisplayName(WarppedComponent)}`
      return Mouse
    }
    
    export default withMouse
    

    效果圖: 使用前
    1.png

    使用后
    2.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末佛嬉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芳室,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刹勃,死亡現(xiàn)場(chǎng)離奇詭異堪侯,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)荔仁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)伍宦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人乏梁,你說(shuō)我怎么就攤上這事次洼。” “怎么了遇骑?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵卖毁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我落萎,道長(zhǎng)亥啦,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任练链,我火速辦了婚禮禁悠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘兑宇。我一直安慰自己,他們只是感情好粱坤,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布隶糕。 她就那樣靜靜地躺著,像睡著了一般站玄。 火紅的嫁衣襯著肌膚如雪枚驻。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天株旷,我揣著相機(jī)與錄音再登,去河邊找鬼。 笑死晾剖,一個(gè)胖子當(dāng)著我的面吹牛锉矢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播齿尽,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼沽损,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了循头?” 一聲冷哼從身側(cè)響起绵估,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤炎疆,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后国裳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體形入,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年缝左,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亿遂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡盒使,死狀恐怖崩掘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情少办,我是刑警寧澤苞慢,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站英妓,受9級(jí)特大地震影響挽放,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蔓纠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一辑畦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腿倚,春花似錦纯出、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至硬贯,卻和暖如春焕襟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饭豹。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工鸵赖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拄衰。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓它褪,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親肾砂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子列赎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容