Seth Spivey
Seth Spivey

Reputation: 367

How to preserve state value after re-running setState

setLabels is called every time a user clicks on a checkbox, resulting in a list of items that match the itemId that has been selected. (EX. if 1.3 is checked, the for loop runs through exportEntries, grabs all the objects values matching the key 1.3)

I then use setState to set filteredArray to the values returned. However, the next time a user clicks on another checkbox, the state is obviously reset.

How would I go about maintaining all previously selected values and adding to the state instead of replacing the current state set?

setLabels = (e, itemId, itemLabel) => {

    let fillerArray = [];
    for (let item of this.state.exportEntries) {

      if (itemId in item) {
        fillerArray.push({ [itemLabel]: item[itemId] });
      }  

      this.setState({
        filteredArray: fillerArray
      });

    }

    console.log(this.state.filteredArray);

    e.preventDefault();
  }

On first Click Output

0: {First: "One"}
1: {First: "Two"}
2: {First: "Three"}
3: {First: "Four"}
4: {First: "Five"}

On second Click Output

0: {Second: "One"}
1: {Second: "Two"}
2: {Second: "Three"}
3: {Second: "Four"}
4: {Second: "Five"}

** EDIT for Arrays of Objects **

  const singleValue = [...this.state.selectedValues];

  for (let item of this.state.exportEntries) {
    if (id in item) {

      // This is pulling values from exportEntries that match the id 
      // of the 'label' selected. So 'First', 'Last', etc... all have 
      // an id that is passed to perform the check here.
      singleValue.push({ [label]: item[id] });
    }
  }

  for (var i = 0; i < singleValue.length; i++) {
    merged.push({ ...this.state.selectedValues[i], ...singleValue[i]})
  }

  // This is where I'm trying to merge the two arrays
  // If I click First & Last this is returned: 

  // (10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
  //   0: {First: "First 1"}
  //   1: {First: "First 2"}
  //   2: {First: "First 3"}
  //   3: {First: "First 4"}
  //   4: {First: "First 5"}
  //   5: {Last: "Last 1"}
  //   6: {Last: "Last 2"}
  //   7: {Last: "Last 3"}
  //   8: {Last: "Last 4"}
  //   9: {Last: "Last 5"}

  // I want this:
  // (5) [{…}, {…}, {…}, {…}, {…}]
  //   0: {First: "First 1" , Last: "Last 1"}
  //   1: {First: "First 2", Last: "Last 2"}
  //   2: {First: "First 3", Last: "Last 3"}
  //   3: {First: "First 4", Last: "Last 4"}
  //   4: {First: "First 5", Last: "Last 5"}

Upvotes: 0

Views: 751

Answers (2)

Dipen Shah
Dipen Shah

Reputation: 26075

I am still having bit hard time understanding your question, but based on my best ability I am trying to suggest you something which might work.

I have created a POC to get around this issue. Main issue is that you current state is updated asynchronously so accessing it immediately after update will not give you updated state.

In my example code, I have added bunch of checkboxes and onChange I am updating state to set all selected checkboxes and in the callback of setSate I am calling setLabels (which I still don't understand what it does).

Important thing here is to do processing after state is updated. Also, looking at your code two things standout in particular:

  1. You are calling this.setState({ filteredArray: fillerArray }); from inside of an array which is redundant and you just want to call it once.
  2. You are updating fillerArray all the time with new value let fillerArray = [];, may be you want let fillerArray = [...this.state.filteredArray]; to preserve old selection.

Take a look at this POC code:

class SomeClass extends Component {
  state = {
    selectedItems: [],
    exportEntries: [],
    filteredArray: []
  };

  setLabels = (itemId, itemLabel) => {
    console.log(this.state.selectedItems);
  };

  handleCheckBoxChange = (isSelected, id, label) => {
    const newSelection = [...this.state.selectedItems];
    if (isSelected) {
      newSelection.push(label);
    } else {
      const index = newSelection.indexOf(label);
      newSelection.splice(index, 1);
    }

    this.setState({ selectedItems: newSelection }, () =>
      this.setLabels(id, label)
    );
  };

  render = () => (
    <div>
      <label>
        <input
          type="checkbox"
          onChange={(e) =>
            this.handleCheckBoxChange(e.target.checked, "name", "First")
          }
        />
        Item 1
      </label>
      <label>
        <input
          type="checkbox"
          onChange={(e) =>
            this.handleCheckBoxChange(e.target.checked, "name", "Second")
          }
        />
        Item 2
      </label>
      <label>
        <input
          type="checkbox"
          onChange={(e) =>
            this.handleCheckBoxChange(e.target.checked, "name", "Third")
          }
        />
        Item 3
      </label>
      <label>
        <input
          type="checkbox"
          onChange={(e) =>
            this.handleCheckBoxChange(e.target.checked, "name", "Fourth")
          }
        />
        Item 4
      </label>
      <div>
        {this.state.selectedItems.map((i) => (
          <p>{i}</p>
        ))}
      </div>
    </div>
  );
}

Upvotes: 1

Auskennfuchs
Auskennfuchs

Reputation: 1737

As I understand your requirement correctly you want to toggle the checkboxes and keep a list of all checked items. You already have the filteredArray. In the setLabelmethod you have to add the current item, if it was checked or remove it from the list, if it was unchecked. The event should give you this information ( e.checked or something).

class SomeClass extends Component {
   state = {
     filteredArray: []
   }

   setLabels = (e, itemId, itemLabel) => {
     const { filteredArray } = this.state
     if(e.checked) {
       this.setState({
          filteredArray: [...filteredArray,{[itemLabel]: item[itemId]}]
       })
     } else {
        this.setState({
           filteredArray: filteredArray.filter(item => item[itemLabel]===undefined)
        })
     }
     console.log(this.state.filteredArray);
 
     e.preventDefault();
   }   
}

If you could check the item in the event, it should not have been added to the array, so it will be appended. Otherwise it was already added and will be filtered out.
The filter looks a little bit strange. You have the itemId, which i presume is unique. So I would suggest, to add the itemId explicitly to the object to have a better filter criteria. The code is not tested, but it should work in this way.

Also keep in mind the console.log will probably doesn't give you the correct output because setState is not synchronous.

Upvotes: 0

Related Questions