Asynchronously concatenating clicked numbers in a cleared string

Viewed 234

The goal of my small React experiment is "clear the initial value of this.state.numString (outputs an empty string), then concatenate the clicked numbers into this.state.numString". To make it execute asynchronously, I took advantage of this.setState's callback where the concatenation of number strings happen.

class App extends Component {
  state = {
    numString: '12'
  }

  displayAndConcatNumber = (e) => {
    const num = e.target.dataset.num;

    this.setState({
      numString: ''
    }, () => {
      this.setState({
        numString: this.state.numString.concat(num)
      })
    })
  }

  render() {
    const nums = Array(9).fill().map((item, index) => index + 1);
    const styles = {padding: '1rem 0', fontFamily: 'sans-serif', fontSize: '1.5rem'};

    return (
      <div>
        <div>
          {nums.map((num, i) => (
            <button key={i} data-num={num} onClick={this.displayAndConcatNumber}>{num}</button>
          ))}
        </div>
        <div style={styles}>{this.state.numString}</div>
      </div>
    );
  }
}

The result was not what I expected; it only adds the current number I click into the empty string then change it into the one I click next, no concatenation of string numbers happens.

3 Answers

You can do something like below to clear the state immediately and concatenate the state with previous state value

this.setState({
    numString: ''
  }, () => {
    this.setState( prevState => ({
     numString: prevState.numString + num
    }));
  });

The code above in your question , in first setState you are setting variable to empty and in the second setState it is concatenating new value with empty string state. Thats why it is not working.

Try something like below:

   class App extends Component {
  state = {
  numString: '12',
  isFirstTime: true
  }

 displayAndConcatNumber = (e) =>  {
    const num =   e.target.dataset.num;
    if(this.state.isFirstTime){

      this.setState({
      numString: '',
      isFirstTime: false
    }, () => {
    this.setState({
    numString:     this.state.numString.concat(num)
     })
    })
    }else{
     this.setState({
     numString:    this.state.numString.concat(num)
  })
   }

 }

   render() {
    const nums =  Array(9).fill().map((item, index) => index + 1);
    const styles = {padding: '1rem 0', fontFamily: 'sans-serif', fontSize: '1.5rem'};

   return (
  <div>
    <div>
      {nums.map((num, i) => (
        <button key={i} data-  num={num} onClick={this.displayAndConcatNumber}>{num}</button>
      ))}
    </div>
    <div style={styles}>{this.state.numString}</div>
  </div>
);
  }
 }

Here is one way of doing this. As I said in my comment you are resetting the string in every setState. So, you need some kind of condition to do that.

class App extends React.Component {
  state = {
    numString: '12',
    resetted: false,
  }

  displayAndConcatNumber = (e) => {
    const num = e.target.dataset.num;

    if ( !this.state.resetted ) {
      this.setState({
        numString: '',
        resetted: true,
      }, () => {
        this.setState( prevState => ({
         numString: prevState.numString.concat(num)
        }))
      })
    } else {
      this.setState(prevState => ({
        numString: prevState.numString.concat(num)
      }))
    }
  }

  render() {
    const nums = Array(9).fill().map((item, index) => index + 1);
    const styles = { padding: '1rem 0', fontFamily: 'sans-serif', fontSize: '1.5rem' };

    return (
      <div>
        <div>
          {nums.map((num, i) => (
            <button key={i} data-num={num} onClick={this.displayAndConcatNumber}>{num}</button>
          ))}
        </div>
        <div style={styles}>{this.state.numString}</div>
      </div>
    );
  }
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Related