解鎖 React 組件化新姿式 - react-call-return

最先的時候,antd 的 Table 用起來是這樣的:javascript

const columns = [
  { dataIndex: 'name', title: 'Name' },
  { dataIndex: 'age', title: 'Age' },
  { dataIndex: 'address', title: 'Address' }
]

<Table columns={columns} data={data} />

只要把列定義和數據傳給 Table 組件,它就會幫你把表格渲染出來。咱們簡單實現一下上面的 Table 組件:java

class Table extends React.Component {
  render() {
    const { columns, data } = this.props;

    return (
      <table>
        <thead>
          <tr>
            {columns.map(col =>
              <th key={col.dataIndex}>{col.title}</th>
            )}
          </tr>
        </thead>
        <tbody>
          {data.map(item => (
            <tr key={item.id}>
              {columns.map(col =>
                <td key={col.dataIndex}>{item[col.dataIndex]}</td>
              )}
            </tr>
          ))}
        </tbody>
      </table>
    );
  }
}

後來,有人就以爲這樣寫不夠 React 啊,我想要用 JSX 來定義列啊。react

圖片描述

好吧,那咱們把上面的 Table 改造一下,容許用 JSX 的方式。git

const Column = () => {};

class Table extends React.Component {
  static Column = Column;

  render() {
    const { children, columns, data } = this.props;

    this.columns = columns || React.Children.map(children, (child) => ({
      dataIndex: child.props.dataIndex,
      title: child.props.title,
    }))

    return (
      <table>
        <thead>
          <tr>
            {this.columns.map(col =>
              <th key={col.dataIndex}>{col.title}</th>
            )}
          </tr>
        </thead>
        <tbody>
          {data.map(item => (
            <tr key={item.id}>
              {this.columns.map(col =>
                <td key={col.dataIndex}>{item[col.dataIndex]}</td>
              )}
            </tr>
          ))}
        </tbody>
      </table>
    );
  }
}

這裏咱們定義了一個什麼都不幹的 Column 組件,而後 Table 組件經過遍歷本身的子元素來把列信息讀出來。這樣就實現 issue 裏要求的功能,很是簡單。github

但是,過了段時間,又有人不滿意了。antd

圖片描述

既然如今支持 JSX 的方式定義列了,那我就想封裝本身的 XxxColumn 組件啊。可是從咱們上面的實現能夠看到,Table 是直接從本身的子元素上讀取列信息的,如今若是 Column 組件被封裝起來,咱們就讀不到 Column 上的信息了。this

這個問題,一直沒有很好的解法。可是,就在昨天,隨着 React 16.1 的發佈,React Team 還發布了兩個實驗性的包 react-reconcilerreact-call-return。其中 react-reconciler 用來實現自定義的 renderer,此處暫且不表。而 react-call-return 這個初看名字徹底不知道什麼東西的包,就能夠解決上面的問題。spa

react-call-return 提供了兩個方法 unstable_createReturnunstable_createCall設計

咱們先來用這兩個方法從新實現一下上面的 Table 組件,再來看咱們的問題是怎麼被解決的。code

const Column = (props) => unstable_createReturn(props);

class Table extends React.Component {
  static Column = Column;

  render() {
    const { children, data } = this.props;

    return (
      <table>
        <thead>
          <tr>
            {unstable_createCall(children, (props, columns) => (
              columns.map(col => (
                <th key={col.dataIndex}>{col.title}</th>
              ))
            ), this.props)}
          </tr>
        </thead>
        <tbody>
          {data.map(item => (
            <tr key={item.id}>
              {unstable_createCall(children, (props, columns) => (
                columns.map(col => (
                  <td key={col.dataIndex}>{item[col.dataIndex]}</td>
                ))
              ), this.props)}
            </tr>
          ))}
        </tbody>
      </table>
    );
  }
}

能夠看到,Column 組件再也不什麼都不作了,它用 unstable_createReturn 建立了一個 return,return 的內容,就是它本身的 props。而後咱們在 Table 裏調用 unstable_createCall, 這個方法的第一個參數是 Table 的子元素,第二個參數是一個 handler 方法,,第三個參數是 Tableprops。由於 Table 的子元素,也就是 Column 返回了一個 return,因此當咱們 call 這些 return 的時候,React 會把 Column return 的結果傳給咱們的 handler 方法,大概就是這個過程 handler(props, children.map((c => c.call() )))。這樣咱們就拿到了 Column 上的 props ,即便 Column 被封裝起來了也不會有問題。

react-call-return 的出現,讓咱們在組件設計上有了更多的可能性,JSX 再也不須要映射 DOM,而是能夠直接來表達數據。

以上全部演示代碼,能夠在這裏找到 https://github.com/yesmeck/neotable

相關文章
相關標籤/搜索