React.js - 使用分页交互进行搜索

时间:2016-03-28 22:08:14

标签: javascript reactjs

我正在使用React.js构建我的第一个应用程序。 该应用程序使用外部电影API(https://www.themoviedb.org/documentation/api)来搜索和列出电影。 该请求采用参数" query"以及可选的" page"。响应已包含结果总数和现有页面总数。 样本响应如下所示:

 {
 "page": 1,
 "results": [
  {
  "adult": false,
  "backdrop_path": "/8uO0gUM8aNqYLs1OsTBQiXu0fEv.jpg",
  "genre_ids": [18],
  "id": 550,
  "original_language": "en",
  "original_title": "Fight Club",
  "overview": "A ticking-time-bomb insomniac and a slippery soap salesman channel primal male aggression into a shocking new form of therapy. Their concept catches on, with underground \"fight clubs\" forming in every town, until an eccentric gets in the way and ignites an out-of-control spiral toward oblivion.",
  "release_date": "1999-10-14",
  "poster_path": "/811DjJTon9gD6hZ8nCjSitaIXFQ.jpg",
  "popularity": 4.39844,
  "title": "Fight Club",
  "video": false,
  "vote_average": 7.8,
  "vote_count": 3527
},
{
  "adult": false,
  "backdrop_path": "/qrZssI8koUdRxkYnrOKMRY3m5Fq.jpg",
  "genre_ids": [
    27
  ],
  "id": 289732,
  "original_language": "zh",
  "original_title": "Zombie Fight Club",
  "overview": "It's the end of the century at a corner of the city in a building riddled with crime - Everyone in the building has turned into zombies. After Jenny's boyfriend is killed in a zombie attack, she faces the challenge of surviving in the face of adversity. In order to stay alive, she struggles with Andy to flee danger.",
  "release_date": "2014-10-23",
  "poster_path": "/7k9db7pJyTaVbz3G4eshGltivR1.jpg",
  "popularity": 1.621746,
  "title": "Zombie Fight Club",
  "video": false,
  "vote_average": 3.5,
  "vote_count": 2
}
],
  "total_pages": 1,
  "total_results": 2
}

我选择的结构如下:

MovieSearchPage
- SearchForm
- SearchResults
--MovieBox
--Pagination
---Page

这是我的解决方案,几乎可以正常工作:

        var SearchResults = React.createClass({
            handlePageChange: function(pageNum) {
                this.props.onPageChange(pageNum);
            },
            render: function() {
                var movies = [];

                this.props.data.results.forEach(function(movie) {
                    movies.push(<MovieBox movie={movie} key={movie.id}/>);
                });

                return (
                   <div>
                   <h3>Search results ({this.props.data.total_results} found)</h3>
                   <Pagination onPageClick={this.handlePageChange} currentPage={this.props.data.page} totalPages={this.props.data.total_pages} totalResults={this.props.data.total_results}/>
                   <div>{movies}</div>
                   </div>
                );
            }
        });

        var SearchBar = React.createClass({
            getInitialState: function() {
                return {query: ''};
            },
            handleQueryChange: function(e) {
                this.setState({query: e.target.value});
            },
            handleSubmit: function(e) {
                e.preventDefault();
                var query = this.state.query.trim();
                if (!query || query.length < 3) {
                    return;
                }
                this.props.onSearch(query);
            },
            render: function() {
                return (
                   <form className="searchForm" onSubmit={this.handleSubmit}>
                    <input 
                        type="text" 
                        name="query" 
                        placeholder="Search for a movie..."
                        value={this.state.query}
                        onChange={this.handleQueryChange}
                    />
                    <input type="submit" value="Search Movie"/>
                   </form>
                );
            }
        });

        var MovieBox = React.createClass({
            render: function() {
                var m = this.props.movie;
                return (
                   <div className="movieBox">
                        <div>{m.original_title} ({m.release_date})</div>
                   </div>
                );
            }
        });

        var Pagination = React.createClass({
            render: function() {
                var pages = [];
                var total = this.props.totalPages;
                for (var i = 1; i <= total; i++) {
                    pages.push(<Page key={i} onPageClick={this.props.onPageClick} pageNum={i} active={(this.props.currentPage == i) || false}/>);
                }
                return (
                    <div className="pagination">{pages}</div>
                );
            }
        });

        var Page = React.createClass({
            handleClick: function(e) {
                var pageNum = e.target.innerHTML;
                this.props.onPageClick(pageNum);
            },
            render: function() {
                return (
                    <a href="#" onClick={this.handleClick} className={this.props.active ? 'active' : ''}>{this.props.pageNum}</a>
                )
            }
        });

        var MovieSearchPage = React.createClass({
            getInitialState: function() {
                return {query: '', pageNum: 1, data: {results: [], total_pages: 0, total_results: 0}};
            },
            initSearch: function(query) {
                this.setState({query: query});
                this.doSearch(query, this.state.pageNum);
            },
            nextPage: function(pageNum) {
                this.setState({pageNum: pageNum});
                this.doSearch(this.state.query, pageNum);
            },
            doSearch: function(query, pageNum) {
                $.ajax({
                    url: this.props.url,
                    dataType : 'json',
                    type: 'POST',
                    data: {query: query, api_key: this.props.apiKey, page: pageNum},
                    success: function(data) {
                        this.setState({data: data});
                    }.bind(this),
                    error: function(xhr, status, err) {
                        console.error(this.props.url, status, err.toString());
                    }.bind(this)
                });
            },
            render: function() {
                return (
                    <div>
                        <SearchBar onSearch={this.initSearch}/>
                        <SearchResults onPageChange={this.nextPage} data={this.state.data}/>
                    </div>
                );
            }
        });

        ReactDOM.render(
            <MovieSearchPage url="http://api.themoviedb.org/3/search/movie" apiKey=""/>,
            document.getElementById('container')
        );

我在这里遇到的问题是,在SearchForm中执行初始搜索,使用&#34; query&#34;但是,我希望通过单击分页(相同的查询字符串,但不同的页码)来执行对API的搜索请求,而无需重新加载页面。

我设法通过将相关的父方法从MovieSearchPage一直传递到Page组件来实现这一点。

这很好,但我坚信,这不是最优雅的解决方案 - 同样的方法被多次传递加上我不得不复制&#34;查询&#34;在MovieSearchBox和SearchForm中都有状态。

请原谅我是个菜鸟,但这是我第一次使用复杂的前端框架。为了达到这个目的,这样做的正确方法是什么?

尽管情况非常普遍,但我无法在网上找到任何内容。

提前致谢!

0 个答案:

没有答案