简体   繁体   中英

How to set max number of items that can be selected in react-select?

I am using CreatableSelect component from react-select. Now users can select as many items as they want, but I want users to select no more than 5 items. How to limit max number of options that can be selected?

<CreatableSelect
  classes={classes}
  styles={selectStyles}
  textFieldProps={{
    label: "Tags"
  }}
  options={suggestions}
  components={components}
  value={this.state.multi}
  onChange={this.handleChange("multi")}
  placeholder=""
  isMulti
/>

I recommend you to use a combination of custom component Menu and isValidNewOption like the following code:

// For this example the limite will be 5
    const Menu = props => {
      const optionSelectedLength = props.getValue().length || 0;
      return (
        <components.Menu {...props}>
          {optionSelectedLength < 5 ? (
            props.children
          ) : (
            <div>Max limit achieved</div>
          )}
        </components.Menu>
      );
    };

    function App() {
      const isValidNewOption = (inputValue, selectValue) =>
        inputValue.length > 0 && selectValue.length < 5;
      return (
        <div className="App">
          <Creatable
            components={{ Menu }}
            isMulti
            isValidNewOption={isValidNewOption}
            options={options}
          />
        </div>
      );
    }

Here a live example .

The idea is to prevent user to access the options after the limit X (5 in the example) and also to prevent the enter keyboard event on create through isValidNewOption prop.

<CreatableSelect
        classes={classes}
        styles={selectStyles}
        options={this.state.multi.length > 4 ? this.state.multi : suggestions}
        components={Components}
        value={this.state.multi}
        placeholder="Tags"
        onChange={(values) => this.setState({ multi: values })}
        isValidNewOption={isValidNewOption} //Look at Marked Answer
        isMulti
/>

Main documentation on how to resolve this issue can be found here:

https://github.com/JedWatson/react-select/issues/1341

const MultiCreatable = ({ options, handleChange, handleCreate, value, maxOptions }) => {
  return (
    <CreatableSelect
      isMulti
      placeholder={placeholder}
      onChange={handleChange}
      options={value.length === maxOptions ? [] : options}
      noOptionsMessage={() => {
        return value.length === maxOptions ? 'You have reached the max options value' : 'No options available' ;
      }}
      onCreateOption={handleCreate}
      value={value}
    />
  )
}

For my case I used normal Select Component from react-select.

<Select 
     options={industries}
     value={industry}
     getOptionLabel={ x => x.id}
     getOptionValue={ x => x.industry}
     onChange={(e) => this.handleSelectChange(e, "industry")}
     isMulti
/>

and handleSelectChange-

handleSelectChange = (e, name) => {
    console.log(e)
    if(e.length < 6){
        return this.setState({
            [name]: e
        })
    }
}

and state -

this.state = { industry: [] }

I am sharing my complete working component I think it can help>>

import React, { useState } from 'react';
import Select from 'react-select';
import makeAnimated from 'react-select/animated';
const animatedComponents = makeAnimated();

const ReactSelect = ({ data }) => {
    const maxOptions = 5;
    const [selectedOption, setSelectedOption] = useState([]);
    const handleTypeSelect = e => {
        setSelectedOption(e);
    };

    return (
        <Select
            onChange={handleTypeSelect}
            getOptionLabel={x => x.name}
            getOptionValue={x => x.slug}
            components={animatedComponents}
            isMulti
            options={selectedOption.length === maxOptions ? [] : data}
            noOptionsMessage={() => {
                return selectedOption.length === maxOptions
                    ? 'You have reached the max options value'
                    : 'No options available';
            }}
            label='tags'
        />
    );
};

export default ReactSelect;

a very easy way to do this is:

<Select
        value={tags}
        onChange={(v) => v.length < 4 ? setTags(v): null}
        isMulti
        name='tags'
        options={options}
        className='basic-multi-select'
        classNamePrefix='select'
      />

just add a simple ternary check for how many items you wants

I found the more simple and clean way, without extra manipulations. This way based on disabling an input component of 'react-select'. Take a closer look at the parameter inputProps .

It can look like:

import Select from 'react-select';
import useField from 'client/components/hooks/useField';

const MultiSelect = ({
  async,
  creatable,
  maxItems,
  ...restProps,
}) => {
  const selectProps = {
    ...restProps,
    // "inputProps: {disabled: boolean}" - our goal
    ...(typeof maxItems === 'number' && maxItems === restProps.value?.length ? {inputProps: {disabled: true}} : {}) 
  };
  const creatableTag = async ? Select.CreatableAsync : Select.Creatable;
  const SelectTag = creatable ? creatableTag : selectTag;

  return (
    <div>
      <SelectTag {...selectProps} />
    </div>
  );
};

const SomeComponentWithMultiSelect = () => {
  const field = useField('data.name'); // field contains: {value: string[], ...}
  const items = [
    {
      label: 'firstValue',
      value: 1,
    },
    {
      label: 'secondValue',
      value: 2,
    },
  ];

  return (
    <MultiSelect
      items={items}
      {...field}
      creatable
      maxItems={1} // {1} as our limit
    />
  )
};

export default SomeComponentWithMultiSelect;

So you don't need to manage excess components.

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