a7dc
a7dc

Reputation: 3416

Only pushing a single element to array

I have a component which you can toggle on/off by clicking on it:

clickHandler = () => {
    this.setState({active: !this.state.active})
    this.props.getSelection(this.state.active)
}

render() {
    const { key, children } = this.props;
    return (
        <button
            key={key}
            style={{...style.box, background: this.state.active ? 'green' : ''}}
            onClick={() => this.clickHandler()}
        >
            {children}
        </button>
    );
}

In the parent component, I pass down a method in order to try and get the value of the selected element pushed into an array, like so:

getSelection = (val) => {
    const arr = []
    arr.push(val);
    console.log(arr, 'arr');
}

My problem is that it only ever adds one element to the array, so the array length is always 1 (even if more than one item has been clicked).

Current result (after you've clicked all three)

console.log(arr, 'arr') // ["Birthday"] "arr"

Expected result (after you've clicked all three)

console.log(arr, 'arr') // ["Birthday", "Christmas", "School achievement"] "arr"

Link to Codepen

Any ideas?

Upvotes: 1

Views: 2996

Answers (2)

FisNaN
FisNaN

Reputation: 2865

There are 2 problems here:

  1. arr is local variable. It doesn't keep the previous onClick result.

  2. setState is an asynchronous event. According to documentation:

    setState() does not always immediately update the component.

    setState((state, props) => {}, () => { /*callback */}) should be used.

class Box extends React.Component {
  state = {
    active: false
  };

  clickHandler = () => {
    this.setState(
      state => ({ active: !state.active }),
      () => {
        this.props.getSelection(this.state.active);
      }
    );
  };

  render() {
    const { children } = this.props;
    return (
      <button
        style={{ ...style.box, background: this.state.active ? "green" : "" }}
        onClick={this.clickHandler}
      >
        {children}
      </button>
    );
  }
}

Minor note:

The key value isn't in the child component's this.props, so you don't have to pass it, but it will not affect the outcome.

In App component, let's create an array in class level for the sake of display:

class App extends React.Component {
  state = {
    needsOptions: ["Birthday", "Christmas", "School achievement"]
  };

  arr = [];

  getSelection = val => {
    this.arr.push(val);
    console.log(this.arr);
  };
}

CodePen here

Upvotes: 0

nico.amabile
nico.amabile

Reputation: 495

Two things: setState is async, so on the next line you might or might not get the latest value, so I recommend changing

clickHandler = () => {
    this.setState({active: !this.state.active})
    this.props.getSelection(this.state.active)
  }

to

clickHandler = () => {
   this.setState({active: !this.state.active}, () => {
      this.props.getSelection(this.state.active)
   })
}

The second argument to the setState is a callback function that will be executed right after the setState is done.

The second thing, on getSelection you are defining a new array each time you get there, so it won't have the values from the previous run. You should store it somewhere.

Upvotes: 1

Related Questions