material-uiのTablesで期間絞り込みを実装(React初心者,useState,useEffect使用例)

##はじめに
みなさんこんにちは、ジャケット大好きしろがみです！

Material-Uiにはデータを表の形にして表示をしてくれるTablesというものがあります。

今回はこのTablesで期間検索が出来るようなコードを書いていこうと思います。参考にしていただければ幸いです。


##実装
https://codesandbox.io/s/nifty-gould-51cnj?file=/src/Table.tsx
Table.tsxが該当コードです。

##ポイント
ポイントの解説をします。部分的にソースを貼るので上のリンクと照らし合わせて見ていただけると嬉しいです。


useStateフックを用いて、絞り込みに使用する値`filterValue`を作成します。
そして、useEffectフックを用いてこの値が変更した時に`filtering()`関数が実行されるようにします。
この`filtering()`関数の中で絞り込みを行います。

```ts
// useStateフックを用いてフィルターに使う値`filterValue`を作成。
const [filterValue, setFilterValue] = useState({
    'begin_date' : '',
    'end_date' : '',    
});

// filterValueが変更した時に、filtering関数、setFilteredData関数を呼び出す。
// rows : フィルターかける前のデータ
useEffect(() => {
　　   // filtering関数 : 絞り込みを行う関数(後述)
    const filteringData = filtering(rows, filterValue);
    setFilteredData(filteringData);
}, [filterValue])
```

`filtering()`関数の中身についてです。
この関数は`filteringByDate()`関数(後述)を呼び出すだけです。
今後、様々な絞り込みをする可能性が大きいので拡張性を考えてこのようにしています。

```ts
function filtering(rawData : { date: string; name: string; }[], filterValue : { [key: string]: string}) {
    let filteringData = filteringByDate(rawData, filterValue);
    return filteringData;     
}
```

`filteringByDate()`関数の中身についてです。
この関数は`beginDate`、`endDate`の間に含まれるデータを返す関数です。

```ts
function filteringByDate(filteringData : { date: string; name: string; }[], filterValue : { [key: string]: string }) {
    if (filteringData === null || filteringData === []) return [];

    const beginDate = (function (begin : string) { 
        // https://262.ecma-international.org/5.1/#sec-15.9.1.1
　　　　　　　　　　　　　　　　// Dateの最小値を宣言
        let res = new Date(-8640000000000000);
        if (begin.length !== 0) {
            const result = begin.split('-');　// Expect format '2000-01-01'
            res = new Date(parseInt(result[0]), parseInt(result[1])-1, parseInt(result[2]));
        }
        return res;
    })(filterValue['begin_date']);

    const endDate = (function (end : string) {
        // https://262.ecma-international.org/5.1/#sec-15.9.1.1
　　　　　　　　　　　　　　　　// Dateの最大値を宣言
        let res = new Date(8640000000000000);
        if (end.length !== 0) {
            const result = end.split('-'); // Expect format '2000-01-01'
            res = new Date(parseInt(result[0]), parseInt(result[1])-1, parseInt(result[2]));
        }     
        return res;          
    })(filterValue['end_date']);

    return filteringData.filter(function (row : { [key: string]: string}) {
        const result = row.date.split('/'); // Expect format '2000/01/01'
        let rowDate = new Date(parseInt(result[0]), parseInt(result[1])-1, parseInt(result[2]));
        return beginDate <= rowDate && rowDate <= endDate;
    });
}
```

入力画面の作成です。
入力画面ですることは、

* 2つ入力フォームを作成する。
* 1つは`filterValue['begin_date']`、もう1つは`filterValue['end_date']`を入力するようにする。

です。こちらもmaterial-uiを使用して実装しました。

```ts
return (
<>
  <Paper>
    <form
      noValidate
      autoComplete="off"
      onSubmit={(e) => e.preventDefault()}
    >
    <>
      <TextField
        id="date"
        label=""
        type="date"
        value={filterValue['begin_date']}
　　　　　　　　　　　　　　　　{/*アイコン設定*/}
        InputProps={{
          startAdornment: (
            <InputAdornment position="start">
              <SearchIcon />    
            </InputAdornment>    
          )    
        }}
        onChange={(e) => {
          // hookが反応しないのでdeep copyをしている    
    　　　　    let tmp = JSON.parse(JSON.stringify(filterValue));
     　　    tmp['begin_date'] = e.target.value;    
     　　    setFilterValue(tmp);
    　　   }}
      />
      〜
      <TextField 
        id="date"
        label=""
        type="date"
        value={filterValue['end_date']}
        {/*アイコン設定*/}
        InputProps={{
          startAdornment: (
            <InputAdornment position="start">
              <SearchIcon />    
            </InputAdornment>    
          )
        }}
        onChange={(e) => {
          // hookが反応しないのでdeep copyをしている
         let tmp = JSON.parse(JSON.stringify(filterValue));
         tmp['end_date'] = e.target.value;    
         setFilterValue(tmp);
       }}
   　　　 />
　　　　　　</>    
 　</form>
　</Paper>
// テーブルは略
</>
)
```

注意するのは下の部分です。
`let tmp = filterValue;`とすると、中身の値自体は変更されますが、`filterValue`自体を表す参照値が変化しないので、絞り込み処理が行われません。
そのため、一度deep copyを行っています。

```ts
        onChange={(e) => {
          // hookが反応しないのでdeep copyをしている    
          // let tmp = filterValue;としてはhookが反応しない。
    　　　　    let tmp = JSON.parse(JSON.stringify(filterValue));
     　　    tmp['begin_date'] = e.target.value;    
     　　    setFilterValue(tmp);
    　　   }}
```

## おわりに
今回はmaterial-uiのTableに期間絞り込み機能の実装例を紹介しました。いかがだったでしょうか？

内容としてはuseStateやuseEffectといったReactの初歩的な内容ですが、手助けになったのなら嬉しいです。また、今回の実装を用いると新しく絞り込みを追加したりということもやりやすいと思います。

個人的な話になりますが、自分はReactを書くのが1年ぶりなのでよっても良い復習になりました。これから仕事でばりばりReactを書くことになると思うので、なにか気づいたことがあればまた共有します！