Render multiple Select components depending on another multi value Select component

Viewed 294

I have a multi value Select component (1) and I want for each item currently in that form, to render another Select component (2) accordingly. Even though removing an item from the (1) component re-renders the amount of (2) components properly, the value displayed in the (2) component is no longer according to its counterpart item in the (1) component. Each options in the (1) component has a "multiplicator" key which needs to be displayed as the defaultValue in its corresponding (2) component. If the user changes the value in a (2) component, the state should update the "multiplicator" key of the options of component (1) and the state as well.

It's rather difficult to describe, please have a look at the following gif and the code below. In the gif, the Select on the right should be "1x", "2x", "2x". My code is probably super naive and if someone has a general input be my guest to improve this react-newbie's code.

Wrongful behaviour

Select arrays:

let optionsIngameMode = [
  { value: 'acorn', label: <div><img src={acorn} height={'20px'} width= 
    {'20px'}/>Acorn</div>, multiplicator: 1 },
  { value: 'rose', label: <div><img src={rose} height={'20px'} width={'20px'}/>Rose</div>, 
    multiplicator: 1 },
  { value: 'bell', label: <div><img src={bell} height={'20px'} width={'20px'}/>Bell</div>, 
    multiplicator: 2 },
  { value: 'shield', label: <div><img src={shield} height={'20px'} width= 
    {'20px'}/>Shield</div>, multiplicator: 2 }
 ];

const optionsMultiplicators = [
  { value: 1, label: '1x'},
  { value: 2, label: '2x'},
  { value: 3, label: '3x'},
  { value: 4, label: '4x'},
  { value: 5, label: '5x'},
  { value: 6, label: '6x'},
  { value: 7, label: '7x'},
  { value: 8, label: '8x'},
  { value: 9, label: '9x'},
  { value: 10, label: '10x'},
];

constructor:

constructor(props) {
  super(props);
  this.state = {
    mode: null,
    lobbyType: null,
    startingCard: null,
    ingameModes: [],
    weis: false,
    crossWeis: null,
    weisAsk: null
  };
  this.handleChange = this.handleChange.bind(this);
};

handleChange function:

  handleChange = (selector, event) => {
   switch (selector) {
     case 'multiplicators': {
       console.log(event);
       optionsIngameMode = optionsIngameMode.map(m => {
         if(m === event.mode) {
           return {...event.mode, multiplicator: event.newMult.value};
         }
         return m;
       });
       console.log(optionsIngameMode);
       const updatedIngameModes = this.state.ingameModes.map(m => {
         if(m === event.mode) {
           return {...event.mode, multiplicator: event.newMult.value};
         }
         return m;
       });
       this.setState({ingameModes: updatedIngameModes});
       break;
     };
     case 'modes': {
       const updatedGameModes = optionsIngameMode.filter(m => {
         for(let mode of event) {
           if(mode.label == m.label) {
             return true;
           }
         }
         return false;
       });
       console.log(updatedGameModes);
       this.setState({ingameModes: updatedGameModes});
       break;
     };
   }
 }

Select component (1):

<Select
    styles={customColumnStyle}
    onChange={newModes => this.handleChange('modes', newModes)}
    closeMenuOnSelect={false}
    options={optionsIngameMode}
    defaultValue={optionsIngameMode}
    noOptionsMessage={() => "No game modes selected"}
    isMulti
    autoFocus
/>

Select component (2):

{this.state.ingameModes?.map(mode =>
    <Select
        options={optionsMultiplicators}
        defaultValue={optionsMultiplicators[mode.multiplicator - 1]}
        onChange={newMult => this.handleChange('multiplicators', {mode, newMult})}
    />
)}
1 Answers

React is not aware of the mode of the Select item in your list unless you provide a unique key. When you remove any item in the list without providing a key to each item, it will remove the last one simply because it can't distinguish between any of them.

key is a special props that is always available even if you don't define it in a JSX element.

{this.state.ingameModes?.map(mode =>
    <Select
        key={mode.value}
        options={optionsMultiplicators}
        defaultValue={optionsMultiplicators[mode.multiplicator - 1]}
        onChange={newMult => this.handleChange('multiplicators', {mode, newMult})}
    />
)}
Related