Component only updates after two clicks React












1















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 (i.e. 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. 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. 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].



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



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



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



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









share|improve this question





























    1















    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 (i.e. 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. 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. 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].



    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



    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



    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



    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









    share|improve this question



























      1












      1








      1








      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 (i.e. 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. 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. 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].



      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



      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



      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



      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









      share|improve this question
















      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 (i.e. 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. 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. 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].



      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



      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



      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



      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






      javascript reactjs






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 24 '18 at 10:08







      HB11

















      asked Nov 24 '18 at 9:01









      HB11HB11

      155




      155
























          1 Answer
          1






          active

          oldest

          votes


















          2














          You should not use current state in setState and should not modify state directly. And you do no actually call this.props.randomClick and it is undefined. 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. They might be buggy or redundant. Looks like you prevent updating App when state.random changes. 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.



          And I guess that getRandomClicks should be setRandomClicks.






          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53456678%2fcomponent-only-updates-after-two-clicks-react%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            2














            You should not use current state in setState and should not modify state directly. And you do no actually call this.props.randomClick and it is undefined. 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. They might be buggy or redundant. Looks like you prevent updating App when state.random changes. 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.



            And I guess that getRandomClicks should be setRandomClicks.






            share|improve this answer




























              2














              You should not use current state in setState and should not modify state directly. And you do no actually call this.props.randomClick and it is undefined. 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. They might be buggy or redundant. Looks like you prevent updating App when state.random changes. 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.



              And I guess that getRandomClicks should be setRandomClicks.






              share|improve this answer


























                2












                2








                2







                You should not use current state in setState and should not modify state directly. And you do no actually call this.props.randomClick and it is undefined. 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. They might be buggy or redundant. Looks like you prevent updating App when state.random changes. 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.



                And I guess that getRandomClicks should be setRandomClicks.






                share|improve this answer













                You should not use current state in setState and should not modify state directly. And you do no actually call this.props.randomClick and it is undefined. 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. They might be buggy or redundant. Looks like you prevent updating App when state.random changes. 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.



                And I guess that getRandomClicks should be setRandomClicks.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 24 '18 at 9:31









                UjinT34UjinT34

                70910




                70910
































                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53456678%2fcomponent-only-updates-after-two-clicks-react%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Costa Masnaga

                    Fotorealismo

                    Sidney Franklin