我目前正在使用与redux的反应构建一个简单的Web。 我已成功将redux与app集成,但是我遇到了2个问题我无法解决。 第一个问题是当用户应用过滤器然后触发请求时,组件保持渲染超过限制。
第二个问题是,当用户更改页面时,首先单击请求给出页面标记,但第二次单击时一切正常。
到目前为止,这些是我的代码。
处理请求的行动
import { BEGIN_FETCH_MOVIES, FETCHED_MOVIES, FETCH_FAILED_MOVIES } from '../constants';
import axios from 'axios';
//fetch movie
const searchQuery = (url) => {
return dispatch => {
//dispatch begin fetching
dispatch({
type : BEGIN_FETCH_MOVIES,
})
//make a get request to get the movies
axios.get(url)
.then((res) => {
//dispatch data if fetched
dispatch({type : FETCHED_MOVIES, payload : res.data});
})
.catch((err) => {
//dispatch error if error
dispatch({type : FETCH_FAILED_MOVIES});
});
}
//return the result after the request
}
export default searchQuery;
主要组件
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { actionSearchMovie, actionSearchSerie } from '../actions'
import DisplayItemMovie from '../components/DisplayItemMovie';
import DisplayItemSerie from '../components/DisplayItemSerie';
import DrPagination from "../components/DrPagination";
import { Layout, Divider, Icon, Spin, Row } from 'antd';
//Home component
class Home extends Component {
constructor(){
super();
this.state = {
moviePage : 1,
seriePage : 1,
urlMovie : '',
urlSerie : ''
}
}
//make request before the render method is invoked
componentWillMount(){
//url
const discoverUrlMovies = 'https://api.themoviedb.org/3/discover/movie?api_key=72049b7019c79f226fad8eec6e1ee889&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1';
//requests
this.fetchMovie(discoverUrlMovies);
}
fetchMovie = ( url ) => {
this.props.actionSearchMovie(url);
}
//handle pagination
handleChangePage = (page) =>{
let url = 'https://api.themoviedb.org/3/discover/movie?api_key=72049b7019c79f226fad8eec6e1ee889&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=' + page;
this.setState({
moviePage : page,
urlMovie : url
}, ()=> this.state);
this.fetchMovie(this.state.urlMovie);
}
//render
render() {
const movies = this.props.movies.results; //movies
let displayMovies; //display movies
const antIcon = <Icon type="loading" style={{ fontSize: 24 }} spin />; //spinner
//if movies and series is undefined, display a spinner
if(movies.results === undefined){
displayMovies = <Spin indicator={antIcon} />
}else {
//map through movies and series and then display the items
displayMovies = movies.results.map((movie) => {
return <DisplayItemMovie key = {movie.id} movie = {movie} />
});
}
return (
<div>
<div className='header'>
Home
</div>
<Divider />
<Layout style = {{paddingBottom : '1rem', margin : '0 auto' }}>
<h1 className = 'title'>Movie</h1>
<Row type = 'flex' style = {{flexWrap : 'wrap'}}>
{displayMovies}
</Row>
<DrPagination total = { movies.total_results } page = { this.handleChangePage } currentPage = { this.state.moviePage } /> </div>
)
}
};
const mapStateToProps = (state) => {
return{
movies : state.search_movies,
}
}
export default connect(mapStateToProps, { actionSearchMovie })(Home);
过滤组件
i
mport React, { Component } from 'react';
import { Row, Col, Select, Button, Divider, InputNumber } from 'antd';
//official genres by movie db
const genres = [
{
"id": 28,
"name": "Action"
},
{
"id": 12,
"name": "Adventure"
}
];
class Filter extends Component {
constructor () {
super();
this.state = {
genre : [],
year : '',
rate : '',
filter : false,
}
}
//handle Genre
handleGenre = (genre) => {
console.log(genre);
this.setState({
genre
}, () => this.state);
}
//handle year
handleYear = (year) => {
this.setState({
year
}, () => this.state);
}
//handle Vote
handleVote = (rate) =>{
this.setState({
rate
}, ()=> this.state);
}
//handle on filter
handleFilter = () =>{
const { genre, year, rate } = this.state;
let url = 'https://api.themoviedb.org/3/discover/movie?api_key=72049b7019c79f226fad8eec6e1ee889&language=en-US&page=1';
if( genre.length === 0 && rate === "" && year === "" ){
alert('No filter is selected');
}else{
this.setState({
filter : true
});
let genreList = '';
if(genre.length > 0){
genreList = '&with_genres='+genre.join();
url += genreList;
}
if(year !== ""){
url += '&primary_release_year='+year;
}
if(rate !== ""){
url += '&vote_average='+rate;
}
//if filter === true pass the new url to the parent component
this.props.url(url);
}
}
render() {
const {filter} = this.state;
let header;
const displayGenres = genres.map((genre) => {
return (
<Select.Option key = { genre.id }>{genre.name}</Select.Option>
);
});
//
if(filter === false){
header = <h1>Top Movies</h1>
}else{
header = <h1>Custom Search</h1>
}
return (
<div>
{header}
<Divider />
<Row type = 'flex' align = 'middle' justify = 'center'>
<Col span = {14}>
<Row type = 'flex' align = 'middle' justify = 'center'>
<Col span = {5}>
<Select
mode = 'tags'
maxTagCount = {1}
style = {{width : '100%'}}
allowClear
placeholder="All Genre"
onChange = {this.handleGenre}
>
{displayGenres}
</Select>
</Col>
<Col span = {3}>
<Select
maxTagCount = {1}
style = {{width : '100%'}}
allowClear
placeholder="All Rate"
onChange = {this.handleVote}
>
<Select.Option value = '5'>>5</Select.Option>
<Select.Option value = '6'>>6</Select.Option>
<Select.Option value = '7'>>7</Select.Option>
<Select.Option value = '8'>>8</Select.Option>
<Select.Option value = '9'>>9</Select.Option>
</Select>
</Col>
<Col span = {3}>
<InputNumber
min = {1980}
max = {2019}
placeholder = 'All Year'
onChange = {this.handleYear}
/>
</Col>
</Row>
</Col>
<Col span = {5}>
<Button type = 'ghost' onClick = {this.handleFilter}>Filter</Button>
</Col>
</Row>
</div>
)
}
};
export default Filter;
分页组件
import React, { Component } from 'react';
import { Pagination } from 'antd';
export default class DrPagination extends Component {
//on change page set the new page
handleChangePage = (page) =>{
this.props.page(page);
}
render() {
return (
<div style = {styles.container}>
<Pagination
current = {this.props.currentPage}
defaultCurrent = {1}
total = {this.props.total}
defaultPageSize = {20}
onChange = {this.handleChangePage}
size = 'small'
showQuickJumper
/>
</div>
)
}
};
const styles = {
container : {
width : '100%',
margin: '1rem',
display: 'flex',
justifyContent: 'center',
alignContent: 'center',
alignItems: 'center',
}
}