LoginSignup
80
86

More than 5 years have passed since last update.

リスト表示+ページングを実装しながらReact.jsを理解してみる

Last updated at Posted at 2015-05-21

jsonを返すAPIを使用して、チュートリアル+αの画面を実装してみます。

前提知識・単語

実装しながら読んだほうが理解しやすいと思います。

React.jsのadventカレンダー

コンポーネント

画面を構成している要素。React.jsは画面をコンポーネントと呼ばれる要素に分けて実装します。
http://qiita.com/koba04/items/4f874e0da8ebd7329701

コンポーネントのライフサイクル

propとstate

1. 画面をコンポーネント分けする

reactJSは画面を構成している要素ごとにパーツ分けをし、コンポーネントという呼び方をしています。

① リストを操作するコンポーネント

② リスト全体のコンポーネント

③ リスト1行のコンポーネント

2. ベースのhtmlを作成する

<html>
<head>
<!-- jQueryはなくてもreactJSは動きますが、このサンプルでは使うので読み込みます -->
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://fb.me/react-0.13.3.js"></script>
<script src="https://fb.me/JSXTransformer-0.13.3.js"></script>
</head>
<body>
    <div id="content">
        <!-- ここに生成されたDOMが書き出されます -->
    </div>
    <script type="text/jsx">
        // ここにReactJSのコードを書いていきます
    </script>
</body>
</html>
apiのレスポンス
{
    total: 20,
    records: [
        {id:1, image:"hoge.jpg", name:"aa"},
        {id:2, image:"boge.jpg", name:"aba"},
        {id:3, image:"goge.jpg", name:"aca"},
        {id:4, image:"doge.jpg", name:"ada"},
        {id:5, image:"qoge.jpg", name:"aea"},
        {id:6, image:"eoge.jpg", name:"afa"},
        {id:7, image:"poge.jpg", name:"aga"},
        {id:8, image:"8oge.jpg", name:"aha"},
        {id:9, image:"0oge.jpg", name:"aia"},
        {id:10, image:"1oge.jpg", name:"aja"},
    ]
}

3. 各コンポーネントを実装する

上記のscriptタグの中に書いていきます。

画面描画部分
React.render(
    // ListBoxというコンポーネントを指定したタグに書き出します
    <ListBox />,
    document.getElementById('content')
);
①リストを操作するコンポーネント
var ListBox = React.createClass({
    // 初期値を設定します
    getInitialState: function() {
        return {data: [], page: 1, apiUrl: 'http://api.com/get'};
    },
    // ajaxのsuccessのfunctionにはbindが必要
    requestData: function() {
        $.ajax({
            url: this.state.apiUrl + '?page=' + this.state.page,
            dataType: 'json',
            cache: false,
            success: function(data) {
                // 既に表示しているデータと新たに取得したデータを連結して表示させる
                var oldData = this.state.data;
                var newData = oldData.concat(data.records);

                // setStateするとDOMが再構築されます
                this.setState({data: newData});
            }.bind(this)
        });
    },

    // スクロールイベント(ここらへんの実装はちょっと怪し目。。)
    onScroll: function(detail) {
        var scrollHeight = $(document).height();
        var scrollPosition = $(window).height() + $(window).scrollTop();

        // 画面最下部までスクロールしたタイミングで
        if ((scrollHeight - scrollPosition) / scrollHeight === 0) {
            // ページ番号更新
            this.setState({page: this.state.page + 1});
            // 次のページのデータを取得
            this.requestData();
        }
    },

    // このコンポーネントがDOMツリーに追加されたタイミングで呼ばれます
    componentDidMount: function() {
        // 1ページ目のデータを取得
        this.requestData();
        // ページングの処理を実装するためにスクロールイベントを追加
        window.addEventListener('scroll', this.onScroll);
    },

    render: function() {
        return (
            // SearchResultLitstのコンポーネントにdataを渡す
            <SearchResultList data={this.state.data} onScroll={this.onScroll} />                                                                                                
        );
    }
});
②リスト全体のコンポーネント
var SearchResultList = React.createClass({
    render: function() {
        // 行数分のリストを格納したノードを生成
        // 親ノードから受け取ったdataはpropsで受け取れます
        var resultNodes = this.props.data.map(function (row) {
            return (
                <SearchResult data={row} />
            );
        });

        return (
             <ul>
                {resultNodes}
             </ul>
        );
    }
});
③リスト1行のコンポーネント
var SearchResult = React.createClass({
    // class名を付けたい場合はclssNameを使います
    render: function() {
        return (
            <li>
                <div className="photo">
                    <img src={this.props.data.image} alt=""></img>
                </div>
                <div className="content">
                    <p className="name">{this.props.data.name}</p>
                </div>
            </li>
        );
    }
});

4. 完成

完成形
<html>
<head>
<!-- jQueryはなくてもreactJSは動きますが、このサンプルでは使うので読み込みます -->
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://fb.me/react-0.13.3.js"></script>
<script src="https://fb.me/JSXTransformer-0.13.3.js"></script>
</head>
<body>
    <div id="content">
    </div>
    <script type="text/jsx">
        var SearchResult = React.createClass({
            // class名を付けたい場合はclssNameを使います
            render: function() {
                return (
                    <li>
                        <div className="photo">
                            <img src={this.props.data.image} alt=""></img>
                        </div>
                        <div className="content">
                            <p className="name">{this.props.data.name}</p>
                        </div>
                    </li>
                );
            }
        });

        var SearchResultList = React.createClass({
            render: function() {
                // 行数分のリストを格納したノードを生成
                // 親ノードから受け取ったdataはpropsで受け取れます
                var resultNodes = this.props.data.map(function (row) {
                    return (
                        <SearchResult data={row} />
                    );
                });

                return (
                     <ul>
                        {resultNodes}
                     </ul>
                );
            }
        });

        var ListBox = React.createClass({
            // 初期値を設定します
            getInitialState: function() {
                return {data: [], page: 1, apiUrl: 'http://api.com/get'};
            },
            // ajaxのsuccessのfunctionにはbindが必要
            requestData: function() {
                $.ajax({
                    url: this.state.apiUrl + '?page=' + this.state.page,
                    dataType: 'json',
                    cache: false,
                    success: function(data) {
                        // 既に表示しているデータと新たに取得したデータを連結して表示させる
                        var oldData = this.state.data;
                        var newData = oldData.concat(data.records);

                        // setStateするとDOMが再構築されます
                        this.setState({data: newData});
                    }.bind(this)
                });
            },

            // スクロールイベント
            onScroll: function(detail) {
                var scrollHeight = $(document).height();
                var scrollPosition = $(window).height() + $(window).scrollTop();

                // 画面最下部までスクロールしたタイミングで
                if ((scrollHeight - scrollPosition) / scrollHeight === 0) {
                    // ページ番号更新
                    this.setState({page: this.state.page + 1});
                    // 次のページのデータを取得
                    this.requestData();
                }
            },

            // このコンポーネントがDOMツリーに追加されたタイミングで呼ばれます
            componentDidMount: function() {
                // 1ページ目のデータを取得
                this.requestData();
                // ページングの処理を実装するためにスクロールイベントを追加
                window.addEventListener('scroll', this.onScroll);
            },

            render: function() {
                return (
                    // SearchResultLitstのコンポーネントにdataを渡す
                    <SearchResultList data={this.state.data} onScroll={this.onScroll} />                                                                                                
                );
            }
        });

        React.render(
            // ListBoxというコンポーネントを指定したタグに書き出します
            <ListBox />,
            document.getElementById('content')
        );
    </script>
</body>
</html>

所感

  • コンポーネントという考え方は、DOM操作が非常にやりやすい
  • それぞれのコンポーネントが独立していて、コンポーネントごとに処理が分かれているので、道筋がたてやすく実装しやすい
  • コードの中にhtml(厳密にはjsx)が混ざってしまって、元のhtmlからの書き直しにコストがかかる

リンク

80
86
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
80
86