Render Props - New pattern in React

在軟件開發的過程當中Code Reuse和可讀性一直是開發人員致力於解決的問題,在React社區中,以往出現了不少的Pattern來解決這個問題,例如Mixin,HOC等等。Render Props是React社區中裏提出的另外的一種Pattern。由React Router的Co-author Michael Jackson 提出,它與HOC等有什麼區別呢?又解決了什麼問題呢?在這篇文章來一探究竟。html

問題的引出

全部的Pattern或者solution都是爲了解決問題而提出的,那麼render props究竟是爲了解決什麼問題呢?咱們先來看一個例子。例如在咱們的App裏咱們實現了這樣一個componentreact

import React from 'react';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      x: 0,
      y: 0
    };
    this.handleMouseMove = this.handleMouseMove.bind(this);
  }

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <div style={{ height: 800 }} onMouseMove={this.handleMouseMove}> <p> The mouse is at ({this.state.x}, {this.state.y}) </p> </div>
    );
  }
}    
複製代碼

Code Sandboxapp

熟悉React的同窗都知道這是一個很簡單的App,他能夠追蹤鼠標位置給到本身。可是問題來了,萬一哪天你的朋友看到這個component,說這個feature很Cool,他的app裏能不能用上呢?那麼該怎麼解決這個問題呢?dom

Code Repeat

首先第一種想法很直接,能夠把這個component作爲一個父Component,另一個Component做爲一個子Component。來看代碼。函數

import React from 'react';

const Dog = ({mouse}) => (
    <div>{mouse.x}, {mouse.y}</div>
)

class MouseDog extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
      x: 0,
      y: 0
    };
    this.handleMouseMove = this.handleMouseMove.bind(this);
  }

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <div style={{ height: 800 }} onMouseMove={this.handleMouseMove}> <p> The mouse is at ({this.state.x}, {this.state.y}) <Dog mouse={mouse} /> </p> </div> ); } } 複製代碼

如今咱們有了兩個Component, Dog 和 DogWithMouse,其實Dog做爲DogWithMouse的子Component,那麼DogWithMouse Component就能利用鼠標位置。好像這樣是把問題解決了,可是若是咱們有另一個Component也想要trace mouse這個feature怎麼辦呢?難道再從新這樣寫一個Component嗎?顯然這種方式是不可取的。this

HOC

若是熟悉react的同窗,可能會想到,這很簡單呀,咱們寫一個High order Component(HOC)就好了,的確HOC能夠解決這個問題,咱們來看看HOC的代碼是怎麼樣的。spa

import React from 'react';

const withMouse = (Component) => {
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        x: 0,
        y: 0
      };
      this.handleMouseMove = this.handleMouseMove.bind(this);
    }

    handleMouseMove(event) {
      this.setState({
        x: event.clientX,
        y: event.clientY
      });
    }

    render() {
      return (
        <div style={{ height: 800 }} onMouseMove={this.handleMouseMove}> <Component mouse={this.state} {...this.props} /> </div> ); } } } const Dog = ({ mouse }) => <div>{mouse.x}, {mouse.y}</div> const EnhancedDog = withMouse(Dog) 複製代碼

在上面的代碼裏,咱們建立了一個withMouse的HOC, 這樣就把這部分邏輯抽象了出來。其餘的Component想要使用咱們mouse tracking的feature。只要將Component傳入HOC就能獲得一個enhance過的Component。pretty cool, right?若是對於HOC不太瞭解,能夠參考react 的官方文檔進行了解。 HOCdebug

HOC彷佛已經完美的解決了問題,可是這種方式有沒有什麼缺點呢?彷佛是有下面的一些缺點。code

  1. 間接的props傳入。 讓咱們從新看一看Dog Component,他的props傳入了mouse,可是在使用這個Component的時候,並無地方傳入了mouse。這是由於mouse是在HOC裏傳入的。你大概會納悶這個Mouse是從那來的。若是維護一個大項目的時候,有時候你就會發現這種間接的props傳入,是有多麼坑了。component

  2. 命名衝突的問題。 用過react的同窗不知道有沒有經歷過大量使用HOC的場景,筆者是見過多層HOC嵌套使用的項目代碼。就像這樣: const NewComponent = withA(withB(withC(...(Component)))) 若是你用過這樣的多層嵌套的HOC,那麼就可能會發生命名衝突的問題。來看代碼:

const withB = (Component) => {
  return class extends React.Component {
    render() {
      return (
        <div style={{ height: 800 }} > <Component mouse={'hehe'} {...this.props} /> </div> ); } } } const EnhancedDog = withB(withMouse(Dog)) 複製代碼

在這裏定義另外的一個HOC withB。它也給props上傳了一個一樣的mouse, 若是再用它來wrap withMouse(Dog) 時,你會發現mouse tracking的feature就不work,可是React不會有任何提示,這是命名衝突的問題。若是你有一個七八層的wrapp到一塊兒的Component,若是出現這樣的命名衝突的問題,那麼就有的debug了。

Render props

好了,既然HOC有這樣的問題,那有沒有什麼方式既能夠作到code reuse,同時也能夠避免HOC的這些問題呢?答案就是Render Props。Render Props是React Traning團隊提出的一種新的React中組織Component的方式,同時也是十分的簡單,咱們來看上邊的例子中,若是用render props如何解決code reuse的問題。

import React from 'react';
import { render } from "react-dom";

class Mouse extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
      x: 0,
      y: 0
    };
    this.handleMouseMove = this.handleMouseMove.bind(this);
  }

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <div style={{ height: 800 }} onMouseMove={this.handleMouseMove}> {this.props.render(this.state)} </div>
    );
  }
}

const DogMouse = () => (
  <Mouse render={({x, y}) => (<div>this mouose is ({x}, {y})</div>})> ) render(<DogMouse/>, document.getElementById('app')) 複製代碼

CodeSandBox

上邊就是render props的一個簡單例子,咱們看完會發現特別的簡單,沒什麼特別的。Mouse就是一個普通的Component,惟一不一樣的是這個Component上定義了一個叫render的prop,是一個函數。在Mouse中會將state做爲參數調用這個函數。而建立的DogMouse一樣也是一個普通的Component,其中定義render的方法,用於定義要render什麼Component。咱們看看render props和HOC相比解決了什麼問題:

  1. code reuse。和HOC同樣的,render props一樣解決了code reuse的問題。把mouse tracking的feature抽象成爲一個Mouse的Component,若是有另一個Component像複用這個feature的時候,簡單的定義另一個Component定義相應的render props就行了。

  2. 間接的props傳入,render props 沒有這個問題,能夠清楚的看到props(x, y)是從什麼地方出入的.

  3. 命名衝突。利用render props是沒有Component wrapp的,因此除非定義Component時候本身命名重複,不然不會有命名衝突的問題。

從上面的例子上看,render props能夠再大多數狀況下替代HOC。react的官方文檔上,目前也正式的介紹了render props,下次讓你想用HOC的時候,來試一試Render props把。

相關文章
相關標籤/搜索