【react-table】v7のhooksを使ってv6のテーブルっぽいものを作る

# はじめに
[react-table](https://github.com/tannerlinsley/react-table) v7のアルファ版がリリースされてから1年以上経過しましたが、ついにv7が正式にリリースされましたね。
v6を使っている方で、アップグレードを検討している方もいるのではないでしょうか。

これまでは、色々な機能を持つテーブルコンポーネントをインポートして利用する形でした。
v7では、ビューとロジックが完全に分離され、ライブラリ側は各種テーブル機能を持つhooksのみを提供するようになります。

アップグレードにより使い方が大きく変わり、テーブルコンポーネント自体は自分で作る必要があります。
v6のテーブルコンポーネントだけ提供されてないかな？と思って探してみたのですが、なさそうだったので自分で作ってみました。

## 環境
- react 16.12
- react-table 7.0.0
- styled-components 5.0.1

# シンプルなテーブルを作る
データを表示するだけの簡単なテーブルを作成します。

基本的には、hooksを使ってテーブルを作成し、cssで見た目を整えていく流れになります。

以下のコード内で使用している `makeData` は、 [サンプル](https://github.com/tannerlinsley/react-table/blob/master/docs/examples.md)等を参考にして適宜配置してください。

## hooksを使ってテーブルを作る
v7の[Basic Sample](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/basic)を参考にテーブルを作成します。

```App.jsx
import React from 'react'
import { useTable } from 'react-table'
import { makeData } from './utils'

const Table = ({ columns, data }) => {
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
  } = useTable({
    columns,
    data,
  })

  return (
    <table {...getTableProps()}>
      <thead>
        {headerGroups.map(headerGroup => (
          <tr {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.headers.map(column => (
              <th {...column.getHeaderProps()}>{column.render('Header')}</th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody {...getTableBodyProps()}>
        {rows.map((row, i) => {
          prepareRow(row)

          return (
            <tr {...row.getRowProps()}>
              {row.cells.map(cell => {
                return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
              })}
            </tr>
          )
        })}
      </tbody>
    </table>
  )
}

const App = () => {
  const columns = [
    // 長いので省略
  ]

  const data = React.useMemo(() => makeData(20), [])

  return (
    <Table columns={columns} data={data} />
  )
}

export default App
```


特にスタイリングしていない、データが並べられただけのテーブルができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544419/8bfe77dd-9714-7bf3-1cfc-a5ebc670ed6c.png)

これでhooksを使ってテーブルを作成することができました。

## 見た目をv6っぽくしていく
cssの適用に[styled-components](https://styled-components.com/)を利用します。
設計用にとりあえず `Div` コンポーネントを作っておきます。

```App.jsx
import React from 'react'
import { useTable } from 'react-table'
import styled from 'styled-components'
import { makeData } from './utils'

const Div = styled.div`
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid rgba(0, 0, 0, 0.1);
`

// ...

const App = () => {
  // ...

  return (
    <Div>
      <Table columns={columns} data={data} />
    </Div>
  )
}
```

以降は[v6のソースコード](https://github.com/tannerlinsley/react-table/tree/v6)（主に `index.styl` ）を参考にスタイリングしていきます。

### table
`<table>` 要素のスタイルを作ります。

```App.jsx
const Div = styled.div`
  // ...

  table {
    flex: auto 1;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    width: 100%;
    border-collapse: collapse;
    overflow: auto;
  }
`
```

ソースによると、table要素をフレックスボックスにして `flex-direction: column` で縦に表示させているようです。

何故このような実装になっているかはよくわかっていませんが、とりあえず真似ておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544419/a8545aba-2334-1a82-972b-2fc9f017d3d5.png)

見た目に大きな変化はありませんが、少しシュッとした気がします。

### thead
ヘッダー部分をスタイリングします。

```App.jsx
const Div = styled.div`
  // ...

  table {
    // ...

    thead {
      flex: 1 0 auto;
      display: flex;
      flex-direction: column;
      -webkit-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
      user-select: none;
      box-shadow: 0 2px 15px 0px rgba(0, 0, 0, 0.15);

      tr {
        flex: 1 0 auto;
        display: inline-flex;

        :not(:last-child) {
          background: rgba(0, 0, 0, 0.03);
          border-bottom: 1px solid rgba(0, 0, 0, 0.05);
        }

        th {
          flex: 1 0 0px;
          white-space: nowrap;
          text-overflow: ellipsis;
          overflow: hidden;
          padding: 5px 5px;
          line-height: normal;
          position: relative;
          border-right: 1px solid rgba(0, 0, 0, 0.05);
          transition: box-shadow .3s cubic-bezier(0.175,  0.885, 0.320, 1.275);
          box-shadow: inset 0 0 0 0 transparent;
        }
      }
    }
  }
`
```

疑似要素を使って `HeaderGroup` にも対応できるようにします。

線や背景の色は `rgba` で透明度を指定して調整します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544419/9c75f08a-bb9b-4f02-b198-f7af748152ba.png)

ヘッダー部分がそれっぽくなりました。

### tbody

```App.jsx
const Div = styled.div`
  // ...

  table {
    // ...

    tbody {
      flex: 99999 1 auto;
      display: flex;
      flex-direction: column;
      overflow: auto;

      tr {
        flex: 1 0 auto;
        display: inline-flex;

        :not(:last-child) {
          border-bottom: solid 1px rgba(0, 0, 0, 0.05);
        }

        // -striped
        :nth-child(odd) {
          background: rgba(0, 0, 0, 0.03);
        }

        // -highlight
        :hover {
          background: rgba(0, 0, 0, 0.05);
        }

        td {
          flex: 1 0 0px;
          white-space: nowrap;
          text-overflow: ellipsis;
          padding: 7px 5px;
          overflow: hidden;
          transition: .3s ease;
          transition-property: width, min-width, padding, opacity;

          :not(:last-child) {
            border-right: 1px solid rgba(0, 0, 0, 0.02);
          }
        }
      }
    }
  }
`
```

今回は `-striped` と `-highlight` クラスを適用したものにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/544419/7b2deb41-cdfc-1689-96d1-f1d3a4b63771.png)

これでシンプルなテーブルが完成しました。


# 感想
今回はbasicなテーブルを作りましたが、ほとんどのケースではpaginationやsortなどの機能が必要不可欠かと思います。

ですがほとんどの場合、hooksで機能作成 ⇒ cssで見た目を整える、という流れは変わらないと思います。

また、見ての通りやることはシンプルですが、v6からそのままアップグレードしようとすると修正量が多いため、実運用でやるのはあまり現実的ではないかなと感じました。

`react-table-6` というパッケージが提供されているみたいなので、それを使ってv7と共存させる形が良さそうです。