Render props、render callback 和高階組件皆可互換

讓 render-xxx 模式均可以互換。javascript

基礎

全部上面提到的三種模式都是爲了處理 mixin 要處理的問題的。在咱們繼續以前,咱們來看一些例子。java

若是你已經掌握這兩個概念,能夠直接跳過這一節看後面的內容react

Render Prop

首先,咱們來寫一個組件來記錄 count,並繪製 render prop 裏的繪製都在 render 方法裏調用了。git

class CounterComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  update = type => {
    if (type === "Inc") {
      this.setState(({ count }) => ({ count: count + 1 }));
    } else if (type === "Dec") {
      this.setState(({ count }) => ({ count: count - 1 }));
    }
  };

  render() {
    return this.props.render({
      ...this.state,
      ...this.props,
      update: this.update
    });
  }
}

注意:callback 屬性也能夠叫個別的名字,不必定就是 render。只不過這個模式叫作 render prop。github

接下來咱們須要一個組件來把須要的內容繪製到屏幕上:this

const Counter = ({ count, update }) => {
  <div>
    <button onClick={() => update("Inc")}>Inc</button>
    {count}
    <button onClick={() => update("Dec")}>Dev</button>
  </div>;
};

Counter組件接受 count 值和一個 update 方法,而後顯示一個增長、一個減小按鈕以及當前的 count 值。code

最後咱們能夠用CounterComponent組件來給Counter增長一些其餘的功能:component

render(
  <CounterComponent render={props => <Counter {...props} />>} />,
  document.getElementById('root')
);

高階組件

上文講解了 render prop 模式。如今來看看怎麼使用高階組件來達到一樣的目的。ip

const withCounter = Component =>
  class Hoc extends React.Component {
    constructor(props) {
      super(props);
      this.state = { count: 0 };
    }

    update = type => {
      if (type === "Inc") {
        this.setState(({ count }) => ({ count: count + 1 }));
      } else if (type === "Dec") {
        this.setState(({ count }) => ({ count: count - 1 }));
      }
    };

    render() {
      return <Component {...this.state} {} />
    }
  };

看上面的例子,咱們能夠要把須要繪製的組件放到另一個全新的組件裏面去。在這個新的組件裏包含了增強的 state 和 props 等內容。get

const CounterExample = withCounter(Counter);
render(<CounterExample />, document.getElementById("root"));

目前咱們已經覆蓋了基本的概念。咱們可使用不一樣的模式來達到相同的目的。下面咱們來關注一下如何讓這幾個模式達到互換的目的。

在 render props 和高階組件之間轉換

有的時候你用的庫提供了一個高級組件,可是你最喜歡的是經過JSX的方式來使用組件。有時會遇到一個提供了 render props 的庫,可是你喜歡的是高階組件。一個頗有趣的事實是這些模式能夠幻想轉換。

咱們來根據上面的例子來加一些方法可讓高階組件和 render props 模式能夠互相轉換。

fromHoc: HOC -> RenderProp
toHoc: RenderProp -> HOC

toHoc方法能夠歸結爲:

toHoc: Render => Comp => props => (
  <Render {...Props} render={props => <Comp {...props} />} />
);

你也能夠看使用 Render Props來做爲替代實現。

它會把一個 render prop 模式轉化爲高階組件。

const withCounter = toHoc(CounterComponent);
const CounterExample = withCounter(Counter);

從高階組件轉化爲 render prop 有一點麻煩。咱們須要把一個 render prop 的組件傳入到高階組件裏。多虧了 Brent Jackon 的這篇文章

fromHoc: hoc => {
  class Render extends React.Component {
    render() {
      return this.props.children(this.props);
    }
  }

  return hoc(Render);
};

或者,使用兩外一種不用 class 的方式。此次要感謝 Rodrigo Pombo 的這個例子

fromHoc: hoc => hoc(props => props.render(props));

咱們能夠寫一個輕量的 helper 方法來實現高階組件和 renderprops 的轉化。注意,咱們也能夠在初始化 toHoc 方法的時候使用另外的 render name,由於 render prop 能夠能有一個不同的名字,或者是一個子 prop。

const iso = {
  fromHoc: hoc => hoc(props => props.render(props)),
  toHoc: Render => Compo => props => (
    <Render {...props} render={props => <Comp {...props} />} />
  )
};

總結

Render prop,回調繪製和高階組件都是能夠互換的。大多數的時候 render props 能夠知足要求。可是你也能夠經過某些方法讓它在這些模式之間互相轉換。

很是感謝 Brent Jackson,Kent C. Dodds 以及 Rodrigo Pombot 提供了fromHoc方法的實現,幫咱們解決了很多的問題。

相關文章
相關標籤/搜索