3

I'm starting to learn it but can't find a solution for this -> I have an input and when the value exceeds 20 characters I want a tooltip to appear with the full value as is typed. I have it all built and kind of working. Problem is that I get a maximum call stack error because the state is being changed on every key press - I'm not sure of the best/correct way to go about it... any help would be greatly appreciated

See my code below and here is the pen

console.clear();

class Input extends React.Component {

  render(){
    return(
      <div>
        <input
          className="main-input"
          onChange={this.props.onChange}
          placeholder={"Tell me something"}
          />
       </div>
    )
  };

}

class Tooltip extends React.Component {

  render(){
    return(
      <div
        className="tooltip"
        data-status={this.props.isShowing}>
        <p>{this.props.TooltipValue}</p>
      </div>
    )
  }
}

class App extends React.Component {
  constructor(){
    super();
    this.state = {
      message: '',
      isShowing: false
    }
  }

  onChange(e) {
      this.setState({
        message: e.target.value
      });
    }

  render(){
    const inputVal = (this.state.message.length);
    if(inputVal >= 20){
      this.setState({isShowing: true})
    } 
    // else {
    //   this.setState({isShowing: false})
    // }

    return(
      <div className="container">
        <Tooltip
          TooltipValue={this.state.message}
          isShowing={this.state.isShowing}
        />
        <Input onChange={this.onChange.bind(this)}/>
      </div>
    )
  }

}


ReactDOM.render(
  <App />,
  document.getElementById('Main')
)
ironmike
  • 143
  • 2
  • 13

2 Answers2

2

Why maximum call stack error when using setState in render function ?

Because when we do setState, React trigger the re-rendering of the component, if you do setstate inside render then after setState, component will render again, again it will find setState again render, it will become a infinite loop, So never do any setState inside render method.

Check this answer for more details about setState behaviour.


Instead of putting that check inside render method, put that inside onChange method, like this:

onChange(e) {
    const inputVal = e.target.value;
    this.setState({
        message: inputVal,
        isShowing : inputVal.length > 20 ? true : false
    });
}

And remove these lines:

if(inputVal >= 20){
    this.setState({isShowing: true})
} 
// else {
//   this.setState({isShowing: false})
// }

Or since the showing of Tooltip depends on the value of input element, you can avoid extra variable and directly check the length of input element with Tooltip property isShowing, like this:

<Tooltip
    TooltipValue={this.state.message}
    isShowing={this.state.message.length >= 20}
/>
Mayank Shukla
  • 100,735
  • 18
  • 158
  • 142
  • 1
    Thanks mate, very informative answer, I didn't know the setState re-renders the component. very helpful, thanks again! – ironmike Apr 23 '17 at 17:33
2

No need to use a separate state for the tooltip since it already depends on the state value you can do it like this

<Tooltip
      TooltipValue={this.state.message}
      isShowing={this.state.message.length > 20}  >     

Also since you are using setState in the render() you are getting an error because setState triggers a re-render and thus beginning an infine loop as soon as you if-condition becomes true.

CODEPEN

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400