对组件的更改道具做出反应,而不是每次都渲染

时间:2018-07-06 20:21:32

标签: javascript reactjs

我正在学习React,并且尝试使用组件显示表中所选项目的详细信息,我的问题是,当我单击表中的Next(已分页)时,状态已更新并重新渲染该组件,如果我多次单击,它将进入队列,并且细节组件在我确实单击过的所有项目之间改变。

我不知道是否有做这种事情的好习惯,我试图搜索类似的东西,但什么都没有。

我正在使用的组件(使用公共API):

import React from 'react';

class Pokemon extends React.Component {
  constructor() {
    super();

    this.state = {
      name: "",
      abilities: [],
      stats: [],
      weight: 0,
      height: 0,
      base_experience: 0,
    };
  }


  fetch_pokemon = () => {
    fetch(this.props.pokemon_url)
    .then(res => res.json())
    .then(res => {
      this.setState({
        name: res.name,
        abilities: res.abilities,
        stats: res.stats,
        weight: res.weight,
        height: res.height,
        base_experience: res.base_experience,
      });
    });
  }

  render() {
    if(this.props.pokemon_url !== ''){
      this.fetch_pokemon();
      return (
        <div>
          <h1>{this.state.name}</h1>
          Weight: {this.state.weight}<br />
          Height: {this.state.height}<br />
          Abilities:
          <ul>
            {this.state.abilities.map(({ability}, index) => {
              return (<li key={index}>{ability.name}</li>);
            })}
          </ul>
        </div>
      );
    }
    else{
      return (<h3>Choose one pokémon from the list...</h3>);
    }
  }
}

export default Pokemon;

我的主要组成部分:

import React, { Component } from 'react';
//import logo from './logo.svg';
import './App.css';
import Pokemon from './Pokemon';


class App extends Component {
  constructor() {
    const i_pokemons = 25;
    super();
    this.state = {
      pokemons: [],
      pokemons_list: [],
      pokemon_show_list: [],
      p_init: 0,
      p_final: i_pokemons,
      pagination: i_pokemons,
      pages: 0,
      status: '',
      enable_buttons: false,
      current_page: 0,
      current_pokemon_url: '',
      URL: `https://pokeapi.co/api/v2/pokemon/?limit=99999`
    };
  }

  componentDidMount(){
    this.fetch_pokemons();
  }

  prev(){
    this.setState({
      current_page: this.state.current_page - 1,
      p_init: this.state.p_init - this.state.pagination,
      p_final: this.state.p_final - this.state.pagination
    }, () => {
      this.fetch_new_page();
    });
  }
  next(){
    this.setState({
      current_page: this.state.current_page + 1,
      p_init: this.state.p_init + this.state.pagination,
      p_final: this.state.p_final + this.state.pagination
    }, () => {
      this.fetch_new_page();
    });
  }

  fetch_new_page = () => {
    const current_id = (this.state.current_page - 1) * this.state.pagination;
    this.setState({
      pokemon_show_list: this.state.pokemons_list.slice(current_id, current_id + 25)
    });
    this.fetch_pokemons();
  }

  fetch_pokemons = callback => {
    this.setState({
      status: 'Waiting for the server and retrieving data, please wait...',
      enable_buttons: false
    });
    return new Promise((resolve, reject) => {
      fetch(this.state.URL)
      .then(res => res.json())
      .then(res => {
        if(!res.detail){
          this.setState({
            pokemons: res,
            pokemons_list: res.results,
            enable_buttons: true,
            status: 'Done',
            pokemon_show_list: res.results.slice(this.state.p_init, this.state.p_final)
          });
          if(this.state.pages === 0){
            this.setState({
              pages: Math.round(this.state.pokemons_list.length / this.state.pagination),
              current_page: 1
            });
          }
          resolve(true);
        }else{
          reject("Error");
          this.setState({status: `Error`});
        }
      })
      .catch(error =>  {
        this.setState({status: `Error: ${error}`});
        reject(error);
      });
    });

  }

  showPokemon({url}){
    this.setState({
      current_pokemon_url: url
    });
  }

  render() {
    console.log("Render");
    return(
      <div className="general">
        <div className="pokemons-info">
          {this.state.status !== '' && this.state.status}
          <br />
            <table className="pokemon-list">
              <thead>
                <tr>
                  <th>Name</th>
                  <th>More info.</th>
                </tr>
              </thead>
              <tbody>

                {this.state.pokemon_show_list.map((pokemon, index) => {
                  return (
                    <tr className="l" key={index}>
                      <td>{pokemon.name}</td>
                      <td><a className="btn btn-secondary" onClick={this.showPokemon.bind(this, pokemon)} href={`#${pokemon.name}`}>More info.</a></td>
                    </tr>
                  );
                })}
            </tbody>
          </table>
          <button className="btn btn-primary" disabled={this.state.current_page <= 1} onClick={this.prev.bind(this)}>Prev</button>
          Page: {this.state.current_page} of {this.state.pages}
          <button className="btn btn-primary" disabled={this.state.current_page === this.state.pages} onClick={this.next.bind(this)}>Next</button>
        </div>
        <Pokemon pokemon_url={this.state.current_pokemon_url}/>
    </div>
    );
  }
}

export default App;
  

随时提供任何建议

1 个答案:

答案 0 :(得分:1)

我重构并清理了一点代码,但是我想下面的代码针对您的需求。 (阅读有关功能组件“无逻辑组件”的更多信息。)

const API = 'https://pokeapi.co/api/v2/pokemon/';
const PAGE_SIZE = 25;
function Status(props) {
  return (
    <div>{props.value}</div>
  )
}

function Pagination(props) {
  return (
    <div>
      <button 
        onClick={props.onPrevious}
        disabled={props.disabled}>
        Prev
      </button>
      <button 
        onClick={props.onNext}
        disabled={props.disabled}>
        Next
      </button>
    </div>
  )
}

function Pokemon(props) {
  return (
    <div>
      <h1>{props.pokemon.name}</h1>
      Weight: {props.pokemon.weight}<br />
      Height: {props.pokemon.height}<br />
      Abilities:
      <ul>
        {props.pokemon.abilities.map(({ability}, index) => {
          return (<li key={index}>{ability.name}</li>);
        })}
      </ul>
    </div>
  )
}


function PokemonTable (props) {
  return (
    <table className="pokemon-list">
      <thead>
        <tr>
          <th>Name</th>
          <th>More info.</th>
        </tr>
      </thead>
      <tbody>
        {props.children}
      </tbody>
    </table>
  );
}


function PokemonRow (props) {
  return (
    <tr>
      <td>{props.pokemon.name}</td>
      <td>
        <a href="#" onClick={() => props.onInfo(props.pokemon)}>
          More info.
        </a>
      </td>
    </tr>
  );
}

class App extends React.Component {
  state = {
    pokemons: [],
    detailedPokemons : {},
    loading: false,
    status : null,
    previous : null,
    next : null
  }

  componentDidMount () {
    this.getPokemons(`${API}?limit=${PAGE_SIZE}`)
  }

  request(url) {
    return fetch(url)
      .then(blob => blob.json());
  }

  getPokemons (url) {
    this.setState(state => ({
      ...state,
      loading : true,
      status : 'Fetching pokemons...'
    }));
    this.request(url)
      .then(response => {
        console.log(response)
        this.setState(state => ({
          ...state,
          previous : response.previous,
          next : response.next,
          pokemons : response.results,
          loading : false,
          status : null
        }))
      })
      .catch(err => {
        this.setState(state => ({
          ...state,
          loading : false,
          status : 'Unable to retrieved pockemons'
        }));
      });
  }

  getPokemonDetail (pokemon) {
    const { detailedPokemons } = this.state;

    const cachePokemon = detailedPokemons[pokemon.name];
    if (cachePokemon !== undefined) { return; }

    this.setState(state => ({
      ...state,
      loading : true,
      status : `Fetching ${pokemon.name} info`
    }));

    this.request(pokemon.url)
      .then(response => {
        this.setState(state => ({
          ...state,
          loading: false,
          status : null,
          detailedPokemons : {
            ...state.detailedPokemons,
            [response.name]: {
              name: response.name,
              abilities: response.abilities,
              stats: response.stats,
              weight: response.weight,
              height: response.height,
              base_experience: response.base_experience
            }
          }
        }))
      })
      .catch(err => {
        console.log(err)
        this.setState(state => ({
          ...state,
          loading : false,
          status : 'Unable to retrieved pockemons'
        }));
      });
  }

  renderPokemons () {
    const { pokemons } = this.state;
    return pokemons.map(pokemon => (
      <PokemonRow
        pokemon={pokemon}
        onInfo={this.handleView}
      />
    ));
  }

  renderDetailPokemons () {
    const { detailedPokemons } = this.state;

    return (
      <ul>
        {Object.keys(detailedPokemons).map(pokemonName => (
          <li key={pokemonName}>
            <Pokemon pokemon={detailedPokemons[pokemonName]}/>
          </li>
        ))}
      </ul>
    )

  }

  handleView = (pokemon) => {
    this.getPokemonDetail(pokemon);
  }

  handlePrevious = () => {
    const { previous } = this.state;
    this.getPokemons(previous);
  }

  handleNext = () => {
    const { next } = this.state;
    this.getPokemons(next);
  }

  render () {
    const { loading, detailedPokemons, status, next, previous } = this.state;

    return (
      <div className='general'>
        <div className="pokemons-info">
          { status && <Status value={status} /> }

          <PokemonTable>
            {this.renderPokemons()}
          </PokemonTable>

          <Pagination
            disabled={loading}
            onPrevious={this.handlePrevious}
            onNext={this.handleNext}
          />

          {
            Object.keys(detailedPokemons).length > 0 &&
            this.renderDetailPokemons()
          }


        </div>
      </div>
    )
  }
}



ReactDOM.render(
  <App />,
  document.querySelector('#app')
);