工程師與貓
ESC
Content
    ↑↓ navigate open esc close
    Published on

    React Component Design - Render Props Component

    Authors
    • avatar
      Name
      Alex Yu

    讓我們一樣以Toggle作為例子,但這次使用Toggle的方式要類似於下面

    function Usage({ onToggle = (...args) => console.log('onToggle', ...args) }) {
      return (
        <Toggle onToggle={onToggle}>
          {({ on, toggle }) => (
            <div>
              {on ? 'The button is on' : 'The button is off'}
              <Switch on={on} onClick={toggle} />
              <hr />
              <button aria-label="custom-button" onClick={toggle}>
                {on ? 'on' : 'off'}
              </button>
            </div>
          )}
        </Toggle>
      );
    }

    要符合這樣的使用方式其實不難,程式碼如下

    import React from 'react';
    import { Switch } from '../switch';
     
    class Toggle extends React.Component {
      state = { on: false };
      toggle = () =>
        this.setState(
          ({ on }) => ({ on: !on }),
          () => this.props.onToggle(this.state.on)
        );
      getStateAndHelpers() {
        return {
          on: this.state.on,
          toggle: this.toggle,
        };
      }
      render() {
        return this.props.children(this.getStateAndHelpers());
      }
    }

    可以看到render functionToggleon state 跟toggle傳給children,由children自行決定要render什麼東西,這樣的方式更有彈性可以決定要顯示什麼樣子的 component,也可以明確知道ontoggle是來自於Toggle。跟Compound component的設計方式比起來,個人覺得這樣的方式有幾個優點:

    • 少了複雜的 Provider/Context state 傳遞關係
    • 不用 validate children 是不是有在Toggle的定義介面裡

    但缺點也很明顯,雖然這樣的設計方式保持最大的彈性讓 children 自行決定要 render 什麼樣的東西,但這既是優點也是缺點,因為這樣的 component 所持有的邏輯過少,children 還是需要自行決定顯示的內容。另外一個缺點則是如果 component 都採用這樣的方式,則會讓 component 的層級過於多層,底層的 children component 最終會很難排查 props 是從何而來。

    雖然還沒介紹到HOC component的設計方式,但這邊引述一下react-router的作者 Michael Jackson 提到這樣做比HOC component好的原因還有:

    • 解決了 naming collision 的問題
    • HOC component比起來提供更多的彈性可以決定render什麼樣的東西,換句話說,這樣的方式直接可以取代HOC component,但是,HOC component卻沒辦法取代Render props

    之後等介紹到HOC component的時候會再更詳細說明優缺點。