组件仅在单击两次后更新

时间:2018-11-24 09:01:21

标签: javascript reactjs

我正在构建一个React应用程序,除其他功能外,该应用程序会在单击按钮时生成一个随机数,然后将JSON对象数组过滤为该随机数索引处的一个(即JSON [random]) 。通常,应该在过滤了JSON对象数组之后重新渲染该应用程序,但是由于某些原因,第一次单击该按钮并选择一个随机变量时,需要两次单击才能更新。从那时起,它会按预期进行更新,每次单击按钮时都会使用新的随机渲染。

我不确定问题是否来自App.js或更低的地方。第一次单击时,它会生成一个新的随机数,并且可以将其保存为状态,但无法立即重新渲染。在随后的点击中,事情似乎是根据先前生成的随机数进行更新的,而新的随机数则被放入队列中。我希望所有这些都可以一次性完成:单击,生成随机,保存到状态,更新以反映新的随机JSON [random]。

这可能与我实施生命周期方法的方式有关,因为我承认我不确定每种方法的所有细微差别,并且只是尝试使用任何一种似乎可以满足我要求的方法。如果您有任何建议,请告诉我...

谢谢!

以下是相关文件:

App.js -在Header.state.randomClicks中注册新点击时生成并存储随机数的地方

class App extends Component {
  constructor(props){
    super(props)
    this.state = {headerLink: "", searchValue: "", random: 0, randomClicks: 0}

    this.generateRandom = this.generateRandom.bind(this);
  }

  getLinkFromHeader = (link) => {
    if (this.state.headerLink !== link) {
      this.setState({
        headerLink: link,
      })
    }
  }

  getSearchValueFromHeader = (string) => {
    this.setState({
      searchValue: string,
    });
  }

  getRandomMax = (max) => {
    this.setState({
      randomMax: max,
    })
  }

  getRandomClicks = (value) => {
    this.setState({
      randomClicks: value,
    })
  }

  generateRandom(number) {
    let random = Math.floor(Math.random() * number) + 1;
    console.log("generateRandom = ", random)
    return random
  }

  shouldComponentUpdate(nextProps, nextState) {
    return this.state.randomClicks !== nextState.randomClicks;
  }

  componentWillUpdate() {}

  componentDidUpdate(prevState) {
    let randomClicks = this.state.randomClicks;
    console.log("this.state.randomClicks: ", this.state.randomClicks)
    // console.log("prevState: ", prevState)
    // console.log("prevState.randomClicks = ", prevState.randomClicks)
    // ^^ is this a bug ? ^^
    let random = this.generateRandom(this.state.randomMax);
    if (this.state.random !== random) {
      this.setState({random: random})
    }
  }

  render() {
    return (
      <div className="App background">
        <div className="content">
          <Header getLinkFromHeader={this.getLinkFromHeader} getSearchValueFromHeader={this.getSearchValueFromHeader} randomClick={this.randomClick} getRandomClicks={this.getRandomClicks}/>
          <TilesContainer link={this.state.headerLink} searchValue={this.state.searchValue} getRandomMax={this.getRandomMax} random={this.state.random} randomClicks={this.state.randomClicks}/>
        </div>
      </div>
    );
  }
}

export default App

Header.js * -每次单击RandomButton时,randomClick计数都会增加

class Header extends Component {
  constructor(props){
    super(props);
    this.state = { selectorLink: "", searchValue: "", randomClicks: 0 }

    this.randomClick = this.randomClick.bind(this);
  }


  getLinkFromSelector = (link) => {
    this.setState({
      selectorLink: link,
    })
  }

  getSearchValue = (string) => {
    this.setState({
      searchValue: string,
    })
  }

  shouldComponentUpdate(nextProps, nextState) {
    console.log("this.state !== nextState: ", this.state !== nextState)
    return this.state !== nextState;
  }

  componentDidUpdate(previousState){
    if(this.state.selectorLink !== previousState.selectorLink) {
      this.props.getLinkFromHeader(this.state.selectorLink);
    }
    this.props.getSearchValueFromHeader(this.state.searchValue);
    this.props.getRandomClicks(this.state.randomClicks);
    console.log("Header Did Update")
  }

  randomClick(){
    this.props.randomClick;
    this.setState({
      randomClicks: this.state.randomClicks += 1,
    });
  }

  render(){
    return(
      <div id="header" className="header">

        <div className="title-div">
          <div className="h1-wrapper title-wrapper">
            <h1>Pokédex Viewer App</h1>
          </div>
        </div>

        <PokedexSelector  getLinkFromSelector={this.getLinkFromSelector}/>

        <SearchBar getSearchValue={this.getSearchValue}/>

        <button type="button" id="random-button" onClick={this.randomClick}>Random Pokémon</button>
        <button type="button" id="show-all-button" onClick={this.showAllClick}>Show All</button>

      </div>
    )
  }
}

export default Header

TilesContainer.js -发送来自App的随机数,并过滤/重新渲染图块列表

class TilesContainer extends Component {

  constructor(props){
    super(props);
    this.state = {
        pokemon: [],
        filteredPokemon: [],
        randomMax: 0,
        showDetails: false,
      };
    this.getPokemon = this.getPokemon.bind(this);
    this.tiles = this.tiles.bind(this);
    this.getPokemon(this.props.link);
  }

  getPokemon(pokedexLink) {
    let link = "";
    (pokedexLink === "")
      ? link = "https://pokeapi.co/api/v2/pokedex/national/"
      : link = this.props.link;
      fetch(link)
      .then(response => response.json())
      .then(myJson => {
        let list = myJson['pokemon_entries'];
        this.setState({
          pokemon: list,
          randomMax: list.length,
        })
        this.props.getRandomMax; // send randomMax to App
      })
  }

  filterPokemon(string) {
    if (string !== "") {
        console.log("string: ", string)
        string = string.toString().toLowerCase()
        let filteredPokemon =  this.state.pokemon.filter(pokemon => {
        const name = pokemon.pokemon_species.name;
        const nameStr = name.slice(0,string.length);
        const number = pokemon.entry_number;
        const numberStr = number.toString().slice(0, string.length);
        return (this.state.random !== 0) ? number.toString() === string : nameStr === string || numberStr === string;
      })
      if (this.props.randomClicks !== 0) { // i.e. using a random
        this.setState({
          filteredPokemon: filteredPokemon,
        })
      } else {
        this.setState({
          filteredPokemon: filteredPokemon,
          randomMax: filteredPokemon.length,
        })
      }
    } else {
      this.setState({
        filteredPokemon: [],
        randomMax: this.state.pokemon.length,
      })
    }
  }


  componentDidUpdate(prevProps, prevState) {
    if (this.props.link !== prevProps.link) {
      this.getPokemon(this.props.link)
    }
    if (this.props.searchValue !== prevProps.searchValue) {
      this.filterPokemon(this.props.searchValue)
    }
    if (this.state.randomMax !== prevState.randomMax){
      this.props.getRandomMax(this.state.randomMax);
    }
    if (this.props.random !== prevProps.random) {
      console.log("TilesContainer random: ", this.props.random)
      this.filterPokemon(this.props.random)
    }
  }

  tiles() {
    console.log("tiles() filteredPokemon: ", this.state.filteredPokemon)
    console.log("tiles() searchValue: ", this.props.searchValue)
    console.log("tiles() random: ", this.props.random)
    if (this.state.pokemon.length > 0) {
      if (this.state.filteredPokemon.length == 0 && this.props.searchValue === ""){
        return (
            this.state.pokemon.map(pokemon => (
            <Tile key={pokemon.entry_number} number={pokemon.entry_number} name={pokemon.pokemon_species.name} url={pokemon.pokemon_species.url}/>
          ))
        )
      } else if (this.state.filteredPokemon.length > 0){
        return (
            this.state.filteredPokemon.map(pokemon => (
            <Tile key={pokemon.entry_number} number={pokemon.entry_number} name={pokemon.pokemon_species.name} url={pokemon.pokemon_species.url}/>
          ))
        )
      }

    }
  }

  render(){
    return (
      <div id="tiles-container"
           className="tiles-container">
             {this.tiles()}
      </div>
    )
  }
}

export default TilesContainer

1 个答案:

答案 0 :(得分:2)

您不应在setState中使用当前状态,也不应直接修改状态。而且您实际上没有调用this.props.randomClick,并且它是未定义的。更改

randomClick(){
    this.props.randomClick;
    this.setState({
        randomClicks: this.state.randomClicks += 1,
    });
}

randomClick(){
    if (typeof(this.props.randomClick) === 'function') this.props.randomClick();
    this.setState(olState => ({
        randomClicks: olState.randomClicks + 1,
    }));
}

还要检查您的shouldComponentUpdate方法。他们可能是越野车或多余的。似乎您在App更改时阻止更新state.random。因此,每次单击按钮时,您都会存储新的随机值,但会使用前一个随机值。因此,对于初始渲染和首次单击,请使用random: 0

我想getRandomClicks应该是setRandomClicks