成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

React 實(shí)現(xiàn) Table 的思考

ChanceWong / 1072人閱讀

摘要:加這兩個(gè)屬性的原因很容易想到,因?yàn)槲覀冊(cè)趯?xiě)表格相關(guān)業(yè)務(wù)時(shí),樣式里面寫(xiě)的最多的就是單元格的寬度和對(duì)齊方式。然而,寫(xiě)的表格后粘貼在中,整行的內(nèi)容都在一個(gè)單元格里面,用寫(xiě)的表格則能夠幾乎保持原本的格式,所以我們這次用了原生的來(lái)寫(xiě)表格。

Table 是最常用展示數(shù)據(jù)的方式之一,可是一個(gè)產(chǎn)品中往往很多非常類(lèi)似的 Table,但是我們碰到的情況往往是 Table A 要排序,Table B 不需要排序,等等這種看起來(lái)非常類(lèi)似,但是又不完全相同的表格。這種情況下,到底要不要抽取一個(gè)公共的 Table 組件呢?對(duì)于這個(gè)問(wèn)題,我們團(tuán)隊(duì)也糾結(jié)了很久,先后開(kāi)發(fā)了多個(gè)版本的 Table 組件,在最近的一個(gè)項(xiàng)目中,產(chǎn)出了第三版 Table 組件,能夠較好的解決靈活性和公共邏輯抽取的問(wèn)題。本文將會(huì)詳細(xì)的講述這種 Table 組件解決方案產(chǎn)出的過(guò)程和一些思考。

Table 的常見(jiàn)實(shí)現(xiàn)

首先我們看到的是不使用任何組件實(shí)現(xiàn)一個(gè)業(yè)務(wù)表格的代碼:

import React, { Component } from "react";

const columnOpts = [
  { key: "a", name: "col-a" },
  { key: "b", name: "col-b" },
];

function SomeTable(props) {
  const { data } = props;

  return (
    
    { columnOpts.map((opt, colIndex) => (
  • {opt.name}
  • )) }
    { data.map((entry, rowIndex) => (
  • { columnOpts.map((opt, colIndex) => ( {entry[opt.key]} )) }
  • )) }
); }

這種實(shí)現(xiàn)方法帶來(lái)的問(wèn)題是:

每次寫(xiě)表格需要寫(xiě)很多布局類(lèi)的樣式

重復(fù)代碼很多,而且項(xiàng)目成員之間很難達(dá)到統(tǒng)一,A 可能喜歡用表格來(lái)布局,B 可能喜歡用 ul 來(lái)布局

相似但是不完全相同的表格很難復(fù)用

抽象過(guò)程

組件是對(duì)數(shù)據(jù)和方法的一種封裝,在封裝之前,我們總結(jié)了一下表格型的展示的特點(diǎn):

輸入數(shù)據(jù)源較統(tǒng)一,一般為對(duì)象數(shù)組

thead 中的單元格大部分只是展示一些名稱,也有一些個(gè)性化的內(nèi)容,如帶有排序 icon 的單元格

tbody 中的部分單元格只是簡(jiǎn)單的讀取一些值,很多單元格的都有自己的邏輯,但是在一個(gè)產(chǎn)品中通常很多類(lèi)似的單元格

列是有順序的,更適合以列為單位來(lái)添加布局樣式

基于以上特點(diǎn),我們希望 Table 組件能夠滿足以下條件:

接收一個(gè) 對(duì)象數(shù)組所有列的配置 為參數(shù),自動(dòng)創(chuàng)建基礎(chǔ)的表格內(nèi)容

thead 和 tbody 中的單元格都能夠定制化,以滿足不同的需求

至此,我們首先想到 Table 組件應(yīng)該長(zhǎng)成這樣的:

const columnOpts =  [
  { key: "a", name: "col-a", onRenderTd: () => {} },
  { key: "b", name: "col-b", onRenderTh: () => {}, onRenderTd: () => {} },
];

其中 onRenderTdonRenderTh 分別是渲染 td 和 th 時(shí)的回調(diào)函數(shù)。

到這里我們發(fā)現(xiàn)對(duì)于稍微復(fù)雜一點(diǎn)的 table,columnOpts 將會(huì)是一個(gè)非常大的配置數(shù)組,我們有沒(méi)有辦法不使用數(shù)組來(lái)維護(hù)這些配置呢?這里我們想到的一個(gè)辦法是創(chuàng)建一個(gè) Column 的組件,讓大家可以這么來(lái)寫(xiě)這個(gè) table:

這樣大家就可以像寫(xiě)HTML一樣把一個(gè)簡(jiǎn)單的表格給搭建出來(lái)了。

優(yōu)化

有了 Table 的雛形,再聯(lián)系下寫(xiě)表格的常見(jiàn)需求,我們給 Column 添加了 widthalign 屬性。加這兩個(gè)屬性的原因很容易想到,因?yàn)槲覀冊(cè)趯?xiě)表格相關(guān)業(yè)務(wù)時(shí),樣式里面寫(xiě)的最多的就是單元格的寬度和對(duì)齊方式。我們來(lái)看一下 Column 的實(shí)現(xiàn):

import React, { PropTypes, Component } from "react";

const propTypes = {
  name: PropTypes.string,
  dataKey: PropTypes.string.isRequired,
  align: PropTypes.oneOf(["left", "center", "right"]),
  width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  th: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
  td: PropTypes.oneOfType([
    PropTypes.element, PropTypes.func, PropTypes.oneOf([
      "int", "float", "percent", "changeRate"
    ])
  ]),
};

const defaultProps = {
  align: "left",
};

function Column() {
  return null;
}

Column.propTypes = propTypes;
Column.defaultProps = defaultProps;

export default Column;

代碼中可以發(fā)現(xiàn) th 可以接收兩種格式,一種是 function,一種是 ReactElement。這里提供 ReactElement 類(lèi)型的 th 主要讓大家能夠設(shè)置一些額外的 props,后面我們會(huì)給出一個(gè)例子。

td 的類(lèi)型就更復(fù)雜了,不僅能夠接收 functionReactElement 這兩種類(lèi)型,還有 int, float, percent, changeRate 這三種類(lèi)型是最常用的數(shù)據(jù)類(lèi)型,這樣方便我們可以在 Table 里面根據(jù)類(lèi)型對(duì)數(shù)據(jù)做格式化,省去了項(xiàng)目成員中很多重復(fù)的代碼。

下面我們看一下 Table 的實(shí)現(xiàn):

const getDisplayName = (el) => {
  return el && el.type && (el.type.displayName || el.type.name);
};

const renderChangeRate = (changeRate) => { ... };

const renderThs = (columns) => {
  return columns.map((col, index) => {
    const { name, dataKey, th } = col.props;
    const props = { name, dataKey, colIndex: index };
    let content;
    let className;

    if (React.isValidElement(th)) {
      content = React.cloneElement(th, props);
      className = getDisplayName(th);
    } else if (_.isFunction(th)) {
      content = th(props);
    } else {
      content = name || "";
    }

    return (
      
        {content}
      
    );
  });
};

const renderTds = (data, entry, columns, rowIndex) => {
  return columns.map((col, index) => {
    const { dataKey, td } = col.props;
    const value = getValueOfTd(entry, dataKey);
    const props = { data, rowData: entry, tdValue: value, dataKey, rowIndex, colIndex: index };

    let content;
    let className;
    if (React.isValidElement(td)) {
      content = React.cloneElement(td, props);
      className = getDisplayName(td);
    } else if (td === "changeRate") {
      content = renderChangeRate(value || "");
    } else if (_.isFunction(td)) {
      content = td(props);
    } else {
      content = formatIndex(parseValueOfTd(value), dataKey, td);
    }

    return (
      
        {content}
      
    );
  });
};

const renderRows = (data, columns) => {
  if (!data || !data.length) {return null;}

  return data.map((entry, index) => {
    return (
      
        {renderTds(data, entry, columns, index)}
      
    );
  });
};

function Table(props) {
  const { children, data, className } = props;
  const columns = findChildrenByType(children, Column);

  return (
    
{hasNames(columns) && ( {renderThs(columns)} )} {renderRows(data, columns)}
); }

代碼說(shuō)明了一切,就不再詳細(xì)說(shuō)了。當(dāng)然,在業(yè)務(wù)組件里,還可以加上公共的錯(cuò)誤處理邏輯。

單元格示例

前面提到我們的 tdth 還可以接收 ReactElement 格式的 props,大家可能還有會(huì)有點(diǎn)疑惑,下面我們看一個(gè) SortableTh 的例子:

class SortableTh extends Component {
 static displayName = "SortableTh";

 static propTypes = {
    ...,
    initialOrder: PropTypes.oneOf(["asc", "desc"]),
    order: PropTypes.oneOf(["asc", "desc", "none"]).isRequired,
    onChange: PropTypes.func.isRequired,
 };

 static defaultProps = {
   order: "none",
   initialOrder: "desc",
 };

 onClick = () => {
   const { onChange, initialOrder, order, dataKey } = this.props;

   if (dataKey) {
     let nextOrder = "none";

     if (order === "none") {
       nextOrder = initialOrder;
     } else if (order === "desc") {
       nextOrder = "asc";
     } else if (order === "asc") {
       nextOrder = "desc";
     }

     onChange({ orderBy: dataKey, order: nextOrder });
   }
 };

 render() {
   const { name, order, hasRate, rateType } = this.props;

   return (
     
{name}
); } }

通過(guò)這個(gè)例子可以看到,thtd 接收 ReactElement 類(lèi)型的 props 能夠讓外部很好的控制單元格的內(nèi)容,每個(gè)單元格不只是接收 data 數(shù)據(jù)的封閉單元。

總結(jié)

總結(jié)一些自己的感想:

前端工程師也需要往前走一步,了解用戶習(xí)慣。在寫(xiě)這個(gè)組件之前,我一直是用 ul 來(lái)寫(xiě)表格的,用 ul 寫(xiě)的表格調(diào)整樣式比較便利,后來(lái)發(fā)現(xiàn)用戶很多時(shí)候喜歡把整個(gè)表格里面的內(nèi)容 copy 下來(lái)用于存檔。然而,ul 寫(xiě)的表格 copy 后粘貼在 excel 中,整行的內(nèi)容都在一個(gè)單元格里面,用 table 寫(xiě)的表格則能夠幾乎保持原本的格式,所以我們這次用了原生的 table 來(lái)寫(xiě)表格。

業(yè)務(wù)代碼中組件抽取的粒度一直是一個(gè)比較糾結(jié)的問(wèn)題。粒度太粗,項(xiàng)目成員之間需要寫(xiě)很多重復(fù)的代碼。粒度太細(xì),后續(xù)可擴(kuò)展性又很低,所以只能是大家根據(jù)業(yè)務(wù)特點(diǎn)來(lái)評(píng)估了。像 Table 這樣的組件非常通用,而且后續(xù)肯定有新的類(lèi)型冒出來(lái),所以粒度不宜太細(xì)。當(dāng)然,我們這樣寫(xiě) Table 組件后,大家可以抽取常用的一些 XXXThXXXTd。

最終,我把這次 Table 組件的經(jīng)驗(yàn)抽離出來(lái),開(kāi)源到 https://github.com/recharts/react-smart-table,希望開(kāi)發(fā)者們可以參考。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/79386.html

相關(guān)文章

  • React 導(dǎo)讀(三)

    摘要:場(chǎng)景為了更清晰的安排年前年后的工作和值班,現(xiàn)在要對(duì)過(guò)年期間人員請(qǐng)假的情況進(jìn)行統(tǒng)計(jì),并且進(jìn)行一個(gè)簡(jiǎn)單的管理。我們現(xiàn)在來(lái)訂閱一個(gè)名為的事件,用來(lái)表示表格中需要展示每條數(shù)據(jù)。 前言 React 導(dǎo)讀(一)React 導(dǎo)讀(二) 在之前 2 篇文章中中學(xué)習(xí)到了寫(xiě)第一個(gè) Web 組件以及常用的生命周期函數(shù)的使用,這篇文章將繼續(xù)之前的目錄,開(kāi)始新的知識(shí)點(diǎn)補(bǔ)充: [x] React 如何編寫(xiě) He...

    zzir 評(píng)論0 收藏0
  • 一次完整react hooks實(shí)踐

    摘要:本次需求其實(shí)就兩個(gè)邏輯輸入篩選項(xiàng)。當(dāng)發(fā)生改變時(shí),重新渲染頁(yè)面首次進(jìn)入頁(yè)面時(shí),無(wú)任何篩選項(xiàng)。關(guān)于的一些,官方也有很棒的文檔寫(xiě)在后面本文通過(guò)工作中的一個(gè)小需求,完成了一次的實(shí)踐,不過(guò)上述代碼依然有很多需要優(yōu)化的地方。 寫(xiě)在前面 showImg(https://segmentfault.com/img/bVbpBgw?w=1000&h=563); 本文首發(fā)于公眾號(hào):符合預(yù)期的CoyPan R...

    kuangcaibao 評(píng)論0 收藏0
  • 19年一些微小計(jì)劃

    摘要:是今年一定要學(xué)的東西這兩年頁(yè)面上用的三方組件多了,寫(xiě)的少了,的一些屬性不太記得了,針對(duì)的學(xué)習(xí)計(jì)劃有兩個(gè)參照的樣式進(jìn)行學(xué)習(xí)參照的組件樣式,學(xué)習(xí)如何處理樣式與組件之間的關(guān)系,規(guī)范自己的寫(xiě)法。 磕磕絆絆工作有幾年了,前端界幾乎每天都有新名詞,令人眼花繚亂,目瞪狗呆。這兩年一直在外包工作,業(yè)務(wù)寫(xiě)的多些,對(duì)js的基礎(chǔ)掌握的還不是很到位。最近深感技術(shù)嗅覺(jué)遲鈍,雖然平時(shí)也有看書(shū)學(xué)習(xí),更多的時(shí)候都是斷...

    harriszh 評(píng)論0 收藏0
  • zepto/jQuery、AngularJS、React、Nuclear演化

    摘要:每個(gè)框架類(lèi)庫(kù)被大量用戶大規(guī)模使用都說(shuō)明其戳中了開(kāi)發(fā)者的剛需。但是未執(zhí)行完的情況下發(fā)生人機(jī)交互雖然不會(huì)報(bào)腳本錯(cuò)誤,但是嚴(yán)重影響用戶體驗(yàn)開(kāi)發(fā)者們被各種爽到之后,這個(gè)問(wèn)題已經(jīng)被拋到了九霄云外。 寫(xiě)在前面 因?yàn)閦epto、jQuery2.x.x和Nuclear都是為現(xiàn)代瀏覽器而出現(xiàn),不兼容IE8,適合現(xiàn)代瀏覽器的web開(kāi)發(fā)或者移動(dòng)web/hybrid開(kāi)發(fā)。每個(gè)框架類(lèi)庫(kù)被大量用戶大規(guī)模使用都說(shuō)明...

    Rindia 評(píng)論0 收藏0
  • 教你如何打好根基快速入手react,vue,node

    摘要:謹(jǐn)記,請(qǐng)勿犯這樣的錯(cuò)誤。由于在之前的教程中,積累了堅(jiān)實(shí)的基礎(chǔ)。其實(shí),這是有緣由的其復(fù)雜度在早期的學(xué)習(xí)過(guò)程中,將會(huì)帶來(lái)災(zāi)難性的影響。該如何應(yīng)對(duì)對(duì)于來(lái)說(shuō),雖然有大量的學(xué)習(xí)計(jì)劃需要采取,且有大量的東西需要學(xué)習(xí)。 前言倘若你正在建造一間房子,那么為了能快點(diǎn)完成,你是否會(huì)跳過(guò)建造過(guò)程中的部分步驟?如在具體建設(shè)前先鋪設(shè)好部分石頭?或直接在一塊裸露的土地上先建立起墻面? 又假如你是在堆砌一個(gè)結(jié)婚蛋糕...

    ddongjian0000 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<