我正在学习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;
随时提供任何建议
答案 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')
);