くずきのblog

技術とか色々

Reactでテーブルをソートするやり方

どうも、くずきです。
Reactでテーブルをソートしようとした際に、View自体のソートはあるんですが
サーバーと連動したやつがなかったので実装してみました。

やりたいこと

f:id:kzkohashi:20171119182707p:plain

上記の画像はすでに完成品だけど、

  • の部分のようなソートボタンを作る
  • ソート中のカラムはどれなのか色をつける
  • ソートが降順か昇順かわかるようにする
  • ソートした際URLが変わるようにする。

が、できれば問題ない。
ソートした場合にデータ取りに行く必要はあるんだけど、以前紹介したURLが変更された際にリソースと再取得するという処理を親のコンポーネントに仕込んでいるので、URLさえ変更すればいい。

kzkohashi.hatenablog.com

コンポーネントの準備

まずは、テーブル用のコンポーネントの準備。
色々省略して、render部分あたりのみ。


render() {
  return(
    <table>
      <thead>
        <tr>
          <th className="prof-image"></th>
          <th className="name">
            <p className="text-left">
              <Link to={TableUtil.getSortUrl("name")} //このカラムがソートされるURL
                    className={TableUtil.getSortClass("name")} //このカラムがソートされている場合のクラス
              >
                <span>名前</span>
              </Link>
            </p>
          </th>
        </tr>
      </thead>
      <tbody>
        {tableContent} // ソートされるごとに変わるtbodyの中身
      </tbody>
    </table>
  );
}

重要なのは、Link部分が - 自身がソートされるURLを持つ - 自身がソートされてる場合デザインを変更するためのクラスの付与 を行う設計する(よくありがちなやつ)。

クエリパラーメーターを連想配列にする

上記のことを行うために、クエリパラメーターを確認しどのカラムがソートされていて、昇順/降順なのか確認する必要がある。
そのために、まずはクエリパラーメーターを扱いやすいように連想配列にする。

Query.js

export function getQueryParam() {
  let vars = {}, max = 0, hash = "", array = "";
  let url = window.location.search;

  hash = url.slice(1).split("&");
  max = hash.length;
  for (let i = 0; i < max; i++) {
    array = hash[i].split("=");
    if (array[1] !== undefined) {
      vars[array[0]] = decodeURI(array[1]);
    }

  }

  return vars;
}

書き方は突っ込まずに、とりあえず格納した連想配列を返している。

自身がソートされるURLを持つ

ソートされるカラムをsc、昇順降順をsdとする。

Table.js

export function getSortUrl(sortColumn) {
  let sortUrl = `${window.location.pathname}?`;
  let query = Query.getQueryParam();

  // クエリパラメーターにscがなかった場合、受け取ったカラムでパラメーターを作る(デフォルト降順[desc])
  if (!query["sc"]) {
    sortUrl += `sc=${sortColumn}&`;
    sortUrl += `sd=desc&`
  }

  for(let key in query) {
  
    // pagenationは除外
    if (key === "page") {
      continue
    }
    
    // scが存在する且つ受け取ったカラムではない場合、ソートするカラムを上書き
    if(key === "sc" && query["sc"] !== sortColumn) {
      sortUrl += `${key}=${sortColumn}&`;
      continue;
    }
    
    // sdが存在する且つscが受け取ったカラムではない場合、降順にする
    if(key === "sd" && query["sc"] !== sortColumn) {
      sortUrl += `${key}=desc&`;
      continue;
    }

    // sdが存在する且つscが受け取ったカラムの場合、ソートの順番を逆にする
    if(key === "sd" && query["sc"] === sortColumn) {
      let sdCol = (query["sd"] === "desc")? "asc" : "desc";
      sortUrl += `${key}=${sdCol}&`;
      continue;
    }

    // その他色々付いているクエリパラーメーターはそのままURLに含める
    sortUrl += `${key}=${query[key]}&`;
  }

  return sortUrl.slice(0, -1);
}

だいぶゴリゴリゴリラな書き方だけど、最適化はそのうち・・・。
自身のカラムと比較してなかったらソートするカラムを追加、ある場合はソートを逆にするなど行なっている。

ソートのデザインを変更するためのクラスの付与

自身がソート対象だった場合、activeを付与してデザインを変更する。

Table.js

export function getSortClass(sortColumn) {
  let className = "sort";
  let query = Query.getQueryParam();
  if (query["sc"] === sortColumn) {
    className = `${className} active ${query["sd"]}`;
  }

  return className;
}

CSS部分

Saasだけと貼っておく。

.sort {
  position: relative;
  display: inline-block;
  padding-right: 14px;
  &:before {
    position: absolute;
    top: 20%;
    right: 0;
    width: 5px;
    height: 5px;
    border-right: 1px solid gray;
    border-bottom: 1px solid gray;
    content: '';
    -webkit-transform: rotate(45deg);
    -ms-transform: rotate(45deg);
    transform: rotate(45deg);
  }

  &.active {
    &:before {
      border-color: red
    }
  }

  &.asc {
    &:before {
      top: 5px;
      -webkit-transform: rotate(225deg);
      -ms-transform: rotate(225deg);
      transform: rotate(225deg);
    }
  }
}

終わりに

ゴリゴリゴリラだからもうちょっとクールに直したい。

今年はRedashのあどべんとかれんだーやることなりました。

qiita.com