API request return html markup, React - Redux

2019-07-25 16:57发布

问题:

I am currently building a simple web using react with redux. I have successfully integrated redux with app however I get 2 issue that I am unable to work out. The first issue is that when the user apply the filter and then trigger the request the component keep rendering exceeding the limit.

The second issue is that when the user change the page the first click the request give a page markup but with the second click everything works.

These are my code so far.

action that take care of the request

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;   

Main component

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);

Filter component

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;

Pagination component

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',
  }
}

This is the data that I get when I try to change the page (it happen only for the first time).