简体   繁体   中英

Have multiple <select> elements share the same options in React?

So I have a React component like:

export default function ExampleComponent() {
    return (
        <div>
            <select required name="select1">
                <option label=" "></option>
                <option value="option1">Option 1</option>
                <option value="option2">Option 2</option>
                <option value="option3">Option 3</option>
            </select>

            <select name="select2">
                <option label=" "></option>
                <option value="option1">Option 1</option>
                <option value="option2">Option 2</option>
                <option value="option3">Option 3</option>
            </select>
        </div>
    );
}

Continuing on for multiple more selects...

I want it so that when I select one option in any of the select tags, it gets removed as an option in all of the others. If I change the selected option, it then becomes available again in all of the others. Does anyone know how I would be able to do this in React?

You have to use state to manage the value and share it between two select s.

 import React, {useState} from 'react' export default function ExampleComponent() { const [value, setValue] = useState('option0') handleChange = (e) => { setValue(e.target.value) } return ( <div> <select value={value} onChange={handleChange}> <option value="option0"></option> <option value="option1">Option 1</option> <option value="option2">Option 2</option> <option value="option3">Option 3</option> </select> <select value={value} onChange={handleChange}> <option value="option0"></option> <option value="option1">Option 1</option> <option value="option2">Option 2</option> <option value="option3">Option 3</option> </select> </div> ); }

Why don't you use state? You can use same datasource for options and also same event handlers for all selects.

export default function ExampleComponent() {
  state = {
    options: [
      {
        name: 'Option 1',
        value: 'option1'
      },
      {
        name: 'Option 2',
        value: 'option2'
      },
      {
        name: 'Option 3',
        value: 'option3'
      }
    ]
  };

  onChangeSelect = event => {
    // do something
    this.setState({
      options: this.state.options.filter(
        option => option.value !== event.target.value
      )
    });
  };

  return (
    <div>
      <select required name="select1" onChange={onChangeSelect}>
        <option label=" "></option>
        {this.state.options.map(option => (
          <option value={option.value}>{option.name}</option>
        ))}
      </select>

      <select name="select2" onChange={onChangeSelect}>
        <option label=" "></option>
        {this.state.options.map(option => (
          <option value={option.value}>{option.name}</option>
        ))}
      </select>
    </div>
  );
}

There may be a more elegant way to do this, but my thought was to keep track of which select have chosen which option with a object called chosenObjects then filtering results based on that information.

import React, {useState} from "react";

export default function ExampleComponent() {
    const selectNames = ["select1", "select2"];
    const [options] = useState([
        {
            label: 'Option 1',
            value: 'option1'
        },
        {
            label: 'Option 2',
            value: 'option2'
        },
        {
            label: 'Option 3',
            value: 'option3'
        }
    ]);


    const [chosenOptions, setChosenOptions] = useState({});

    const isChosenByOther = (optionValue, selectName) => {
        for (let key in chosenOptions) {
            if (key !== selectName) {
                if (chosenOptions[key] === optionValue) {
                    return true;
                }
            }
        }
        return false;
    };

    const handleChange = (ev) => {
        setChosenOptions({...chosenOptions, [ev.target.name]: ev.target.value});
    };

    return (
        <div>
            {selectNames.map((name, index) => {
                return (
                    <select name={name} key={index} onChange={handleChange} value={chosenOptions[name] || ''}
                            required={index === 0}>
                        <option value=''/>
                        {options.filter(({value}) => !isChosenByOther(value, name))
                            .map(({label, value}, oIndex) =>
                                <option value={value} key={oIndex}>{label}</option>)
                        }
                    </select>
                )
            })}
        </div>
    );
}

You can also conditionally disable your empty string valued option after something is chosen:

<div>
    {selectNames.map((name, index) => {
        return (
            <select name={name} key={index} onChange={handleChange} value={chosenOptions[name] || ''}
                    required={index === 0}>
                <option value='' disabled={chosenOptions[name]}>Choose Option</option>
                {options.filter(({value}) => !isChosenByOther(value, name))
                    .map(({label, value}, oIndex) =>
                        <option value={value} key={oIndex}>{label}</option>)
                }
            </select>
        )
    })}
</div>

You need to track the state of multiple Select elements, and then conditionally render the options for each one.

I'll use some bits from the other answers and show how to make it work. Edited to keep the options in the same order.

import React, {useState} from "react";
function ExampleComponent() {
    const [myState, setMyState] = useState({});
    const options = [
        {
            name: 'Option 1',
            value: 'option1'
        },
        {
            name: 'Option 2',
            value: 'option2'
        },
        {
            name: 'Option 3',
            value: 'option3'
        }
    ]

    // list of select names
    const selectNames = [
        "select1",
        "select2"
    ]

    handleChange = (e) => {
        // update the value for the given select
        myState[e.target.name] = e.target.value
        setMyState(myState)
    }

    const inUseValues = Object.values(myState);

      console.log(inUseValues)

    // get the options for given name
    getOptions = (name) =>
        options.filter(option => !inUseValues.includes(option.value) || myState[name]==option.value)
        .map(option => (
            <option key={option.value} value={option.value}>{option.name}</option>
        ))

    // render all select controls
    const selectRender = selectNames.map(name => (
        <select key={name} required name="{name}" value={myState[name]} onChange={handleChange}>
            <option label=" "></option>
            {getOptions(name)}
        </select>
        ))

    return (
        <div>
            {selectRender}
        </div>
    )

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM