かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

ReactとTypeScriptでデータグリッド表示するのに使えるreact-datagrid

業務で使おうと思ったらそれなりのデータグリッド部品が必要になりますよね。そんなとき、ちょっと我慢すれば使えそうなreact-datagridを紹介したいと思います。

react-datagrid

npm install react-datagrid --save
tsd install react-datagrid

で導入が可能なデータグリッド部品です。公式サイトは以下のページになります。

A carefully crafted datagrid for React

サンプルを見ると、大体どんなことが出来るのかわかるようになっています。

A carefully crafted datagrid for React

ライセンスはMITになるので、ライセンス的にも使いやすいライブラリです。

React + TypeScriptで使う

npmとtsdコマンドを打ち込んで必要なファイルを準備したら、node_modules/react-datagrid/dist/index-no-normalize.min.cssをプロジェクトにコピーしてプロジェクトに含めます。そしてindex.htmlで参照するように設定します。今回はWebAPIの呼び出し結果をバインドしたいので、whatwg-fetchもインストールして参照しておきました。index.htmlは以下のような雰囲気になります。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta charset="utf-8" />
    <title>Hello world</title>
    <link href="index-no-normalize.min.css" rel="stylesheet" />
</head>
<body>
    <div id="content"></div>
    <script src="scripts/fetch.js"></script>
    <script src="bundle.js"></script>
</body>
</html>

WebAPI

こんな感じのASP.NET WebAPIを準備しておきます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Web.Http;

namespace ReactApplication3.Controllers
{
    public class PeopleController : ApiController
    {
        public IHttpActionResult Get()
        {
            Thread.Sleep(5000);
            var source = Enumerable.Range(1, 1000)
                .Select(x => new Person
                {
                    Id = x,
                    Name = $"おかずき{x}",
                    Age = x % 30 + 30,
                });
            return Ok(source);
        }
    }

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

react-datagridを使う

idPropertyとdataSoruceとcolumnsは必須っぽいです。TypeScriptだと割とインテリセンスがきくので簡単に書けます。色気を出してフィルタリングも実装してみました。onFilterに何かしら追加するとフィルタリングが有効になるみたいです。

/// <reference path="../typings/whatwg-fetch/whatwg-fetch.d.ts" />
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import ReactDataGrid = require('react-datagrid');

class Person {
    constructor(public id: number, public name: string, public age: number) {
    }
}

interface DataGridComposerState {
    data: Person[];
    loading: boolean;
}

class DataGridComposer extends React.Component<{}, DataGridComposerState> {
    private originalData: Person[];
    constructor(props: {}) {
        super(props);
        this.state = { data: [], loading: true };
    }

    private dataSource() {
        return fetch('api/People', {
            headers: {
                'Accept-Type': 'application/json'
            }
        })
            .then(x => x.json());
    }

    private handleFilter(column, value, allFilterValues) {
        var data = this.originalData;
        Object.keys(allFilterValues).forEach(name => {
            var f = allFilterValues[name] as string;
            if (f === '') {
                return;
            }

            data = data.filter(x => {
                return x[name].toString().indexOf(f) === 0;
            });
        });

        this.setState({ data: data } as DataGridComposerState);
    }

    componentDidMount() {
        this.dataSource().then(x => {
            this.originalData = x as Person[];
            this.setState({
                data: x as Person[],
                loading: false
            } as DataGridComposerState);
        });
    }

    render() {
        return (
            <ReactDataGrid
                idProperty='id'
                dataSource={this.state.data} 
                columns={[
                    { name: 'name', title: '名前', width: 200 },
                    { name: 'age', title: '年齢', width: 200 }
                ]}
                pagination={false} 
                style={{ height: 300 }}
                loading={this.state.loading}
                liveFilter={true}
                onFilter={this.handleFilter.bind(this)}/>
        );
    }
}

class App extends React.Component<{}, {}> {
    render() {
        
        return (
            <div>
                <h1>OK Google.</h1>
                <DataGridComposer />
            </div>);
    }
}

ReactDOM.render(
    <App />,
    document.getElementById('content'));

実行してみる

こんな感じでローディング中の表示がされます。

f:id:okazuki:20160102200504p:plain

そしてデータが表示されます。

f:id:okazuki:20160102200538p:plain

フィルタリングもばっちり。

f:id:okazuki:20160102200622p:plain

いまいちなところと注意点

style属性のheightでデータグリッドの高さを決めれるのですが、ここはnumber型じゃないとダメです。'500px'とかって書くと効きません。あと、ローカライズが弱いです。一部表示が英語のままでもいいかなというのが許容できない場合は使えないと思います。