0

I've been trying to build a select box that will be added dynamically from a button click. I can add and remove the select boxes, but the issue is, whenever I remove an item from the array, the selected option on the remaining elements does not update accordingly. if I remove an element from the last index, it's working perfectly, the problem occurs whenever the item removes from the beginning or in the middle. here's what I have so far.

type RailcardsProps = {
  value: string,
  fieldName: string,
  count: string;
}

const railCardsList = [
  {
    text: 'Select Rail card',
    value: ''
  },
  {
    text: 'Child Saver',
    value: 'childSaver'
  },
  {
    text: 'Adult Saver',
    value: 'adultSaver'
  },
  {
    text: 'Senior Railcard',
    value: 'seniorRailcard'
  },
  {
    text: 'Network Railcard',
    value: 'networkRailcard'
  },
]

const PassangerDropdown = () => {
  const [railCards, setRailCards] = useState<RailcardsProps[]>([]);

  const handleRailCardCLick = () => {
    const railCardsArray = railCards;
    const count = railCardCount;
    setRailCards(railCardsArray.concat({
      value: 'Select Rail Card',
      fieldName: `railcard-${railCardCount}`,
      count: '1'
    }));

    setRailCardCount(count + 1);
  }

  useEffect(() => {
    console.log(railCards);
  }, [railCards])

  const handleSelectRailcard = (event: ChangeEvent<HTMLSelectElement>) => {
    const value = (event.target.value);
    const updatedRailCards = railCards;
    const index = event.target.getAttribute('data-index')!;
    updatedRailCards[+index].value = value;
    setRailCards([...updatedRailCards]);
  }

  const handleRailcardCount = (event: ChangeEvent<HTMLSelectElement>) => {
    const count = (event.target.value);
    const updatedRailCards = railCards;
    const index = event.target.getAttribute('data-index')!;
    updatedRailCards[+index].count = count;
    setRailCards([...updatedRailCards]);
  }

  const handleRemoveRailcard = (index: number) => {
    const updatedRailCards = railCards;
    const count = railCardCount;
    updatedRailCards.splice(index, 1);
    setRailCardCount(count - 1);
    console.log(index);
    setRailCards([...updatedRailCards]);
  }

return (
<div className="passanger-dropdown-item--left w-100">
                  <button type="button" className="btn custom-link mb-2" onClick={handleRailCardCLick}>Add Railcards</button>
                  {
                    railCards.map(({ value, fieldName, count }, index) => (
                      <div key={index} className="passanger-dropdown-item--content p-0">
                        <select
                          defaultValue={value}
                          name={fieldName}
                          className="form-control passanger-dropdown-item--select w-70 mr-1"
                          data-index={index}
                          onChange={handleSelectRailcard} >
                          {
                            railCardsList.map(({ value, text }) => (
                              <option key={value} defaultValue={value}>{text}</option>
                            ))
                          }
                        </select>
                        <select
                          defaultValue={count}
                          name={`${fieldName}-count`}
                          className="form-control passanger-dropdown-item--select w-30"
                          data-index={index}
                          disabled={value === 'Select Rail Card'}
                          onChange={handleRailcardCount} >
                          {
                            ['1', '2', '3', '4', '5', '6', '7', '8', '9'].map((item) => (
                              <option key={item} defaultValue={item}>{item}</option>
                            ))
                          }
                        </select>
                        <button
                          type="button"
                          className="btn passanger-dropdown-item--btn"
                          onClick={() => handleRemoveRailcard(index)}>x</button>
                      </div>
                    ))
                  }
                </div>)
}

in this site passenger dropdown kind of thing what I'm trying to achieve, in the passenger dropdown we can add or remove rail cards, How can I solve this issue?

2 Answers 2

2

i think your problem is that you depends on the array index when removing and adding new items which is considered antipattern in react because in most cases it could causes in unexpected results. so i suggest to create random index to avoid this like i did let id = Math.floor(Math.random() * 1000000);. however, i strongly recommend to use some module to generate unique ids like uuid.

also i can see you have some struggle with state mutation. try my approache

type RailcardsProps = {
   value: string,
   fieldName: string,
   count: string;
}

const railCardsList = [
  {
    text: "Select Rail card",
    value: ""
  },
  {
    text: "Child Saver",
    value: "childSaver"
  },
  {
    text: "Adult Saver",
    value: "adultSaver"
  },
  {
    text: "Senior Railcard",
    value: "seniorRailcard"
  },
  {
    text: "Network Railcard",
    value: "networkRailcard"
  }
];

const PassangerDropdown = () => {
  const [railCards, setRailCards] = useState<RailcardsProps[]>([]);
  
  const handleRailCardCLick = () => {
    let id = Math.floor(Math.random() * 1000000);
    setRailCards([
      ...railCards,
      {
        value: "Select Rail Card",
        fieldName: `railcard-${railCards.length + 1}`,
        count: 1,
        id
      }
    ]);
  };

  const handleSelectRailcard = (event: ChangeEvent<HTMLSelectElement>) => {
    const index = event.target.getAttribute("data-index");
    const value = event.target.value;
    const updatedRailCards =
      railCards &&
      railCards.map((i) => {
        if (i.id === +index) {
          i.value = value;
        }
        return i;
      });
    setRailCards(updatedRailCards);
  };

  const handleRailcardCount = (event: ChangeEvent<HTMLSelectElement>) => {
    const index = event.target.getAttribute("data-index");
    const count = event.target.value;

    const updatedRailCards =
      railCards &&
      railCards.map((i) => {
        if (i.id === +index) {
          i.count = count;
        }
        return i;
      });
    setRailCards(updatedRailCards);
  };

  const handleRemoveRailcard = (id: number) => {
    const updatedRailCards = railCards && railCards.filter((r) => r.id !== id);
    setRailCards(updatedRailCards);
  };

  return (
    <div className="passanger-dropdown-item--left w-100">
      <button
        type="button"
        className="btn custom-link mb-2"
        onClick={handleRailCardCLick}
      >
        Add Railcards
      </button>
      {railCards &&
        railCards.map(({ value, fieldName, count, id }, index) => (
          <div key={id} className="passanger-dropdown-item--content p-0">
            <select
              defaultValue={value}
              name={fieldName}
              className="form-control passanger-dropdown-item--select w-70 mr-1"
              data-index={id}
              onChange={handleSelectRailcard}
            >
              {railCardsList.map(({ value, text }) => (
                <option key={value} defaultValue={value}>
                  {text}
                </option>
              ))}
            </select>
            <select
              defaultValue={count}
              name={`${fieldName}-count`}
              className="form-control passanger-dropdown-item--select w-30"
              data-index={id}
              disabled={value == "Select Rail Card"}
              onChange={handleRailcardCount}
            >
              {["1", "2", "3", "4", "5", "6", "7", "8", "9"].map((item) => (
                <option key={item} defaultValue={item}>
                  {item}
                </option>
              ))}
            </select>
            <button
              type="button"
              className="btn passanger-dropdown-item--btn"
              onClick={() => handleRemoveRailcard(id)}
            >
              x
            </button>
          </div>
        ))}
    </div>
  );
};

live demo sample

Sign up to request clarification or add additional context in comments.

7 Comments

This makes sense, Thanks for clearing out my thoughts over the state mutation. I used uuid and it works perfectly now
Hello, can you share your solution in a live demo? I am having the same issue
hi @CatarinaNogueira, it is already shared on live demo. codesandbox.io/s/hungry-cori-2gwv9
@CatarinaNogueira, it works great on my end, please try it again
Yes, it works, it was my firewall that was blocking! Thank you :)
|
1

In your Select element, add value={value}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.