我正在使用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中都有状态。
请原谅我是个菜鸟,但这是我第一次使用复杂的前端框架。为了达到这个目的,这样做的正确方法是什么?
尽管情况非常普遍,但我无法在网上找到任何内容。
提前致谢!