简体   繁体   English

组件仅在单击两次后更新

[英]Component only updates after two clicks React

I am building a React app that - among other things - generates a random number when a button is clicked and then filters an array of JSON objects to only the one at the index of that random number (ie JSON[random]). 我正在构建一个React应用程序,除其他功能外,该应用程序会在单击按钮时生成一个随机数,然后将JSON对象数组过滤为该随机数索引处的一个(即JSON [random])。 Normally the app is supposed to re-render after the array of JSON objects is filtered, but for some reason, on the first time the button is clicked and a random is picked, it requires two clicks to update. 通常,应该在过滤了JSON对象数组之后重新渲染该应用程序,但是由于某些原因,第一次单击该按钮并选择一个随机变量时,需要两次单击才能更新。 From then on it updates as expected, with a new random rendering each time the button is clicked. 从那时起,它会按预期进行更新,每次单击按钮时都会使用新的随机渲染。

I'm not sure if the problem is coming from App.js or somewhere lower down. 我不确定问题是来自App.js还是更低的地方。 On the first click, it generates a new random and supposedly saves this to state, but fails to re-render right away. 第一次单击时,它会生成一个新的随机数,并且可以将其保存为状态,但无法立即重新渲染。 On subsequent clicks, things seem to update based on the previously-generated random, while a new random is put in the queue. 在随后的点击中,事情似乎是根据先前生成的随机数进行更新的,而新的随机数则被放入队列中。 I would prefer the this all happens in one go: click, generate random, save to state, update to reflect the new random à la JSON[random]. 我希望所有这些都可以一次性完成:单击,生成随机,保存到状态,更新以反映新的随机JSON [random]。

This might have something to do with the way I have implemented lifecycle methods, as I'm admittedly not sure of all the nuances of each and have just tried to use whichever ones seemed to do what I wanted. 这可能与我实现生命周期方法的方式有关,因为我承认我不确定每种方法的所有细微差别,并且只是尝试使用任何一种似乎可以满足我要求的方法。 If you have any suggestions there, please let me know... 如果您有任何建议,请告诉我...

Thanks! 谢谢!

Here are the relevant files: 以下是相关文件:

App.js - where the random is generated and stored when a new click is registered in Header.state.randomClicks 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* - where the randomClick count is incremented each time RandomButton is clicked 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 - where the random number from App is sent and the tiles list is filtered/re-rendered 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

You should not use current state in setState and should not modify state directly. 您不应在setState使用当前状态,也不应直接修改状态。 And you do no actually call this.props.randomClick and it is undefined. 而且您实际上并没有调用this.props.randomClick ,它是未定义的。 Change 更改

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

to

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

Also check your shouldComponentUpdate methods. 还要检查您的shouldComponentUpdate方法。 They might be buggy or redundant. 他们可能是越野车或多余的。 Looks like you prevent updating App when state.random changes. 看起来您在state.random更改时阻止更新App So every time you click the button you store the new random value but use the previous one. 因此,每次单击按钮时,您都会存储新的随机值,但会使用前一个随机值。 So for the initial render and for the first click you use random: 0 . 因此,对于初始渲染和首次单击,请使用random: 0

And I guess that getRandomClicks should be setRandomClicks . 而且我猜getRandomClicks应该是setRandomClicks

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM