【React】動的テーブル　セル結合

動的テーブルでセル結合をする

目指すかたち
civilization VI wiki
![civ6table.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236420/652aac1b-d9c5-e309-37fe-947f9531c70b.jpeg)

テーブルの特徴
・一つのセルに要素が２つ以上あったりなかったりする
・同要素でもまとまってないセルが存在する（コスト）

想定する動き
・DBからテーブルデータ引っ張ってきて(fetch)ならべる

問題
・DBから引っ張ってきて普通に並べると一列ずつになっちゃう

```react:
const items = [
  {A: "A1", B: "B1" , C: "C1"},
  {A: "A1", B: "B2" , C: "C1"},
  {A: "A1", B: "B2" , C: "C2"},
  {A: "A2", B: "B1" , C: "C1"},
  {A: "A2", B: "B1" , C: "C2"},
];

const ItemsTableOld = () => {
  return(
    <table border="1">
      {items.map((item) => {
        return(
          <tr>
            <td >{item.A}</td>
            <td >{item.B}</td>
            <td >{item.C}</td>
          </tr>
        )
      })}
    </table>
  )
}
```
結果
![tebleold.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236420/d8212fa6-7de3-22c7-8360-8d4307e331f2.jpeg)

同一の要素は一つにまとめたい。
rowspanを設定してセルを結合するのだが、広げた分よけいに描画しないようにする。
DBにrowspanの値と描画するかどうかのbooleanを入れておくしかない？

```react:
const items = [
  {A: "A1",drawA:true , rowA:3 , B: "B1" ,drawB:true  ,rowB:1, C: "C1",drawC:true ,rowC:2},
  {A: "A1",drawA:false, rowA:0 , B: "B2" ,drawB:true  ,rowB:2, C: "C1",drawC:false,rowC:0},
  {A: "A1",drawA:false, rowA:0 , B: "B2" ,drawB:false ,rowB:0, C: "C2",drawC:true ,rowC:1},
  {A: "A2",drawA:true , rowA:2 , B: "B1" ,drawB:true  ,rowB:2, C: "C1",drawC:true ,rowC:1},
  {A: "A2",drawA:false , rowA:0 , B: "B1" ,drawB:false ,rowB:0, C: "C2",drawC:true ,rowC:1},
];

const ItemsTable = () => {
  return (
    <table border="1">
      {items.map((item) => {
        return(
          <tr>
            {item.drawA && <td rowSpan={item.rowA}>{item.A}</td>}
            {item.drawB && <td rowSpan={item.rowB}>{item.B}</td>}
            {item.drawC && <td rowSpan={item.rowC}>{item.C}</td>}
          </tr>
        )
      })}
    </table>
  )
}
```

結果
![tablenew.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236420/e3bf5555-2315-a798-8210-90a4919d39d3.jpeg)

一応動的にテーブルを描画しつつセル結合もできている。
問題点
・テーブルがきたない
・rowspanを定義するrowA,rowB,rowCは省略できないか
・オブジェクトからあらかじめ同一の要素を抜き出してその数を数えて、それをrowspanとできないか
⇒隣り合ってないけど同一の要素を抽出してrowspanがおかしくなるので難しい
・描画するかどうかを定義するboolean drawA,drawB,drawCはロジックで外に出せないか
⇒出せました
・同一要素一つ目だけdraw:trueとして、それ以外をdraw:falseとするにはどうしたらよいか
⇒テーブルに持たせればおk
・同一要素でもまとめたくない要素の存在
⇒やはりテーブルにrowspanを持たせて任意で変えられるようにするのが良さそう

改善１：booleanは消せる

```react:
const items = [
  {A: "A1", rowA:3 , B: "B1" ,rowB:1, C: "C1",rowC:2},
  {A: "A1", rowA:0 , B: "B2" ,rowB:2, C: "C1",rowC:0},
  {A: "A1", rowA:0 , B: "B2" ,rowB:0, C: "C2",rowC:1},
  {A: "A2", rowA:2 , B: "B1" ,rowB:2, C: "C1",rowC:1},
  {A: "A2", rowA:0 , B: "B1" ,rowB:0, C: "C2",rowC:1},
];

const ItemsTable = () => {
  return (
    <table border="1">
      {items.map((item) => {
        return(
          <tr>
            {item.rowA > 0 && <td rowSpan={item.rowA}>{item.A}</td>}
            {item.rowB > 0 && <td rowSpan={item.rowB}>{item.B}</td>}
            {item.rowC > 0 && <td rowSpan={item.rowC}>{item.C}</td>}
          </tr>
        )
      })}
    </table>
  )
}
```

データベースから取ってくるのも目標なので、こんなん用意しました。

![Web キャプチャ_29-5-2021_235517_127.0.0.1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236420/a9e031ae-29cf-93fd-9612-5f3391233323.jpeg)

このレコードをreactでフェッチして同じように並べてみます。

```react
import React,{useState, useEffect} from 'react';
import axios from 'axios';

const FetchedTable = () => {
  const[alphas, setAlphas] = useState([])
  useEffect(() => {
    axios.get('http://127.0.0.1:8000/api/v1/alphabet')
    .then(res => setAlphas(res.data));
  },[]);

  return(
    <table border="1">
      {alphas.map(alpha => {
        return(
          <tr>
            {alpha.rowspanA > 0 && <td rowSpan={alpha.rowspanA}>{alpha.characterA}</td>}
            {alpha.rowspanB > 0 && <td rowSpan={alpha.rowspanB}>{alpha.characterB}</td>}
            {alpha.rowspanC > 0 && <td rowSpan={alpha.rowspanC}>{alpha.characterC}</td>}
          </tr>
        )
      })}
  </table>
  )
}

/*index = 0の場合描画する*/
/*前回ループと値が違う　array[index - 1].X !== item.X　場合描画する*/

const DinamicTable = () => {
  return(
    <>
      <p>fetched table</p>
      <FetchedTable/>
    </>

  )
}

export default DinamicTable;
```

結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236420/c9687422-1198-462c-2f82-3472e3bdd14c.png)

とりあえず一つ目標達成。

アイデア
①バックエンドの方でカンマ区切りの文字列を登録
②フロント側でsplitする
これならrowspanを使わなくて済む