Reactのmaterial-tableで外部APIを利用している場合のソート実装でやったこと

# 目的

Reactのmaterial-tableで、dataに外部APIを利用している場合のソート実装とスタイル調整の覚書です

# バージョン
下記バージョンで確認しました

- React 17.0
- material-table 1.69.2
- material-ui-core 4.11.3
- lodash 4.17.21

# 公式サンプル

https://material-table.com/#/docs/features/remote-data

データに外部APIを利用する方法は 公式のRemote Data Previewの通りですが、公式サンプルではソートが出来ません。
これは、変更があるたびに新しいquery objectで関数が呼ばれるのですが、呼び出しているAPI reqres.in でソートに対応していないからです。

# ソートの実装方法

ソートするとデータ変更が走り `query` に `orderBy` と `orderDirection` が連携されるので、これを利用してソートを実装します。

https://github.com/mbrn/material-table/blob/5e9ef9affbc83db80301c4edee4306aabcc3701f/src/material-table.js#L346-L356

## 案1 APIでソートに対応する
リクエストパラメータで指定された項目、並び順でソートしてレスポンスを返すAPIを作成し、UIから呼び出す方法です

### UIからAPIに連携するパラメータ
ページングするので、ソート情報と合わせてページング情報もAPIに連携します。
`query.pageSize` と `query.page` はoptionsでページングを利用しない設定をしない場合でも存在するので必須とみなして良いでしょう。
初期表示時など `query.orderBy` と `query.sortDirection` が存在しない場合はAPIのリクエストパラメータに含める必要はありません。

下記リクエストパラメータをAPIに連携すれば良いと思いますが、実際の要件によるので確認してください。

- ページング情報：必須
  - 1ページあたりの件数 (`query.pageSize` )
  - 現在何ページか (`query.page` + 1)
- ソート情報：任意
  - ソートする項目 (`query.orderBy.field`)
  - ソート順 (`query.orderDirection`) 


### APIでの対応
#### UI側からのリクエストパラメータを受け付ける

セキュリティ対策で、リクエストを受け付けた後はバリデーションを行いましょう。

#### データ検索処理
連携されたリクエストパラメータを利用して、データの検索を行います。
動的SQLでページング、ソートを行うことになるでしょう。
ソートする項目はUI上のフィールド名のため、データベースのカラム名とUI上のフィールド名の変換を持つ必要があるかもしれません。

#### APIレスポンスの作成と返却
公式のUsageに従い下記をレスポンスで返却します。JSONが便利です。
https://material-table.com/#/docs/features/remote-data

- 検索結果
    - material-tableの `columns` で指定するフィールドが必要です
- テーブルに表示する全レコード件数
    - ページング、ソート有無関係ない、テーブルの全件数です
    - ページングする場合、何件中何件の形で表示されるため必要です
- 現在のページ数

## 案2 API返却結果をUI側でソートする
API仕様上ソート結果を反映できない場合は、lodashなどを使って `result.data` をソートします。

初期表示時など `query.orderBy` と `query.sortDirection` が存在しない場合は、ソートせず `result.data` をそのままdataに設定します。

# おまけ：スタイルの調整
### hover,activeの色の変更
スタイル次第でこのように、項目をクリックしてソートした後に文字が見えない状態になることがあります。

![スクリーンショット 2021-03-01 23.17.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/45710/86426b8e-04b0-3f42-14a1-b5cb6175f2ac.png)

- hoverしたときは背景とほぼ同じ色が利用される
- ソート後にマウスを項目から離した後も、`MuiTableSortLabel-active` の背景色が黒
のが理由なので、Material UIのテーマをオーバライドして `MuiTableSortLabel` のスタイルを付けます。

```
  const theme = createMuiTheme({
    overrides: {
      MuiTableSortLabel: {
        root: {
          color: "#fff",
          "&:hover": {
            color: "#fff !important"
          }
        },
        active: {
          color: "#fff !important"
        }
      }
    }
  });
```

ここではとりあえず表示させたいので `!important` を付けて優先順位をあげましたが、StackOverflowの2件目の回答『Solution for your problem is following:』も良さそうです。

https://stackoverflow.com/questions/56682287/how-to-custom-color-text-and-icon-in-tablesorttext-component-of-material-ui


### ソートアイコンのカスタマイズ
ソートアイコンのサイズが大きい、背景色やスタイルによってはソート操作後にアイコンが見にくい、といった時はカスタマイズできます。
DevelopersIOさんのこちらの記事の通りです。

https://dev.classmethod.jp/articles/customize-material-table-sort-arrow-to-be-recognized/

# 動作サンプル
CodeSandboxのサンプルです。このページの説明の『案2 API返却結果をUI側でソートする』と『スタイルの調整』を実装しています。
修正していろいろな挙動を試してみてください。
https://codesandbox.io/s/material-table-remote-data-sort-demo-vgfhm?file=/src/App.js

# 参考
- [material-table 公式 | Remote Data](https://material-table.com/#/docs/features/remote-data)
- [material-table 公式 | GitHub Issue #844](https://github.com/mbrn/material-table/issues/844#issuecomment-513589705)
- [Material UI公式 | TableSortLabel API](https://material-ui.com/api/table-sort-label/)
- [material-tableでソート矢印アイコンをユーザーに認識されやすいようにカスタマイズしてみた](https://dev.classmethod.jp/articles/customize-material-table-sort-arrow-to-be-recognized/)
- https://stackoverflow.com/questions/56682287/how-to-custom-color-text-and-icon-in-tablesorttext-component-of-material-ui