【问题标题】:Multiselect in a dropdown list in React在 React 的下拉列表中进行多选
【发布时间】:2024-05-02 04:00:02
【问题描述】:

我试图能够从我创建的下拉列表中选择不同的值。所以下面的代码实际上是显示人的名字,但我希望能够选择多个。

<Form.Group controlId="exampleForm.ControlSelect4">
    <Form.Label> Names </Form.Label>
    <Form.Control as="select" value={this.state.selectedNames} 
                  onChange={this.updateTable}>
        {this.state.names.map((name) => <option key={name.value} value={name.value}> {name.display } </option>)}
    </Form.Control>
</Form.Group>

【问题讨论】:

  • 你在使用 react-bootstrap 吗?

标签: javascript reactjs checkbox dropdown bootstrap-multiselect


【解决方案1】:

这类似于设置单个值,但该值是一个数组,而不是字符串或数字。

首先,您必须更改 value 和 onChange 函数正在执行的操作。对于值,将默认状态设置为数组。对于onChange,我们将在选中项目的情况下设置它,它设置了新状态,如下所示:

javascript

state = {
 selectedNames:[]
}

onChange = (e) => {
  e.preventDefault()
  this.setState(prevState => ({selectedNames: [...prevState.selectedNames, e.target.value]})
}

希望这会有所帮助!

【讨论】:

    【解决方案2】:

    事件需要添加到单个选项,多选需要相当多的行来实现。这里有一些 sn-p 仅适用于您可能关心的部分。如您所见,我没有使用任何第三方控件。

          <div className="_action">
            <span
              role="button"
              aria-pressed="false"
              tabIndex={0}
              onClick={() => { onClear() }}
            >
              Clear Selection
            </span>
          </div>
          {options.map(option => (
            <div
              role="presentation"
              className="_item"
              key={option.value}
              onClick={() => { onSelect(option.value) }}
            >
              <Checkbox value={isChecked(option, value)} />
              <span className="_label">{option.label}</span>
            </div>
          ))}
    

    onSelectonClear 可能由父/自组件提供,

      const onSelect = useCallback(v => {
        const e = {
          target: {
            name,
            value: toggleValueInOptions(value, v, options)
          }
        }
        onChange(e)
      }, [name, value, options, onChange])
    
      const onClear = useCallback(() => {
        const e = { target: { name, value: [] } }
        onChange(e)
      }, [name, onChange])
    

    还有一个实用函数toggleValueiInOptions

    const toggleValueInOptions = (value, key, options) => {
      if (!value) return []
    
      const values = value.slice()
      const index = values.indexOf(key)
      if (index >= 0) {
        values.splice(index, 1)
      } else {
        values.push(key)
      }
    
      if (!options) return values
    
      return options.reduce((acc, option) => {
        if (values.includes(option.value)) {
          acc.push(option.value)
        }
        return acc
      }, [])
    }
    
    export default toggleValueInOptions
    

    ===============

    供您参考,这是父 MultiSelect 的完整代码。

    import React, { useState, useCallback, useRef } from 'react'
    import PropTypes from 'prop-types'
    import { useClickOutside } from '../../utils'
    import InputBase from '../InputBase'
    import Pills from './Pills'
    import MultiSelection from './MultiSelection'
    import MultiSelectStyle from './MultiSelectStyle'
    import SelectIcon from './SelectIcon'
    import { optionsType, valuesType } from './optionsType'
    import toggleValueInOptions from './toggleValueInOptions'
    import valueToItems from './valueToItems'
    import SelectionSummary from './SelectionSummary'
    
    /**
     * @memberof MultiSelect
     * @param {Object} _                Props
     * @param {elementType} _.Style       Style component
     * @param {string} _.name             Input name
     * @param {valueType[]} _.value         Input value of array
     * @param {func} _.onChange           Value change event
     * @param {optionsType[]} _.options     Options array
     * @param {elementType} _.Selection=MultiSelection    Component for dropdown selection
     * @param {bool} _.disabled=false     Input disabled flag
     * @param {bool} _.width=auto         Input width
     * @param {string} _.placeholder      Input placeholder
     * @param {elementType} _.DropdownIcon=DropdownIcon   Compoent for dropdown icon component
     * @param {number} _.pillVisibleMax   Max pill displayed
     * @param {elementType} _.Summary=SelectionSummary    Component for dropdown summary
     */
    const MultiSelect = ({
      Style, name, value, options, onChange,
      Selection, disabled, width, placeholder,
      DropdownIcon, pillVisibleMax, Summary,
      ...props
    }) => {
      const [focus, setFocus] = useState(false)
    
      const onExpand = useCallback(() => {
        if (!disabled) setFocus(true)
      }, [disabled])
      const onCollapse = useCallback(() => { setFocus(false) }, [])
      const ref = useRef()
      useClickOutside({ ref, handler: () => { onCollapse() } })
    
      const onSelect = useCallback(v => {
        const e = {
          target: {
            name,
            value: toggleValueInOptions(value, v, options)
          }
        }
        onChange(e)
      }, [name, value, options, onChange])
    
      const onClear = useCallback(() => {
        const e = { target: { name, value: [] } }
        onChange(e)
      }, [name, onChange])
    
      const after = <DropdownIcon focus={focus} onExpand={onExpand} onCollapse={onCollapse} />
    
      const phText = value.length ? '' : placeholder
      const vText = (value.length > pillVisibleMax) ? `${value.length} Selected` : ''
    
      return (
        <Style ref={ref}>
          <InputBase
            value={vText}
            placeholder={phText}
            disabled={disabled}
            readOnly
            after={after}
            onFocus={onExpand}
            width={width}
            {...props}
          />
          {!vText && (
            <Pills
              items={valueToItems(value, options)}
              onSelect={onSelect}
              disabled={disabled}
            />
          )}
          {focus && (
            <Selection
              value={value}
              options={options}
              onSelect={onSelect}
              onClear={onClear}
              Summary={Summary}
            />
          )}
        </Style>
      )
    }
    
    MultiSelect.propTypes = {
      Style: PropTypes.elementType,
      name: PropTypes.string,
      value: valuesType,
      options: optionsType,
      onChange: PropTypes.func,
      Selection: PropTypes.elementType,
      disabled: PropTypes.bool,
      width: PropTypes.string,
      placeholder: PropTypes.string,
      DropdownIcon: PropTypes.elementType,
      pillVisibleMax: PropTypes.number,
      Summary: PropTypes.elementType
    }
    
    MultiSelect.defaultProps = {
      Style: MultiSelectStyle,
      name: '',
      value: [],
      options: [],
      onChange: () => { },
      Selection: MultiSelection,
      disabled: false,
      width: '',
      placeholder: '',
      DropdownIcon: SelectIcon,
      pillVisibleMax: 99,
      Summary: SelectionSummary
    }
    
    export default MultiSelect
    

    【讨论】:

      【解决方案3】:

      (我假设您使用的是react-bootstrap

      回答

      您需要将另一个道具传递给Form.Control 以指定您希望您的选择允许多项选择。您可以使用multiple={true} 或简写为multiple(两者等效)来执行此操作。

      This example 在他们的文档中使用了多选,这可能会有所帮助。

      我把这个sandbox 放在一起,可以说明如何使用它。

      什么是棘手的

      使用 react 处理状态可能很困难。众所周知,表单具有挑战性,因为它们涉及大量内部状态。

      其他尝试

      • 使用反应挂钩来管理状态而不是类组件。在我看来,这可以更容易地处理状态

      我的例子中的错误

      • 当只有一个选择并且您尝试取消选择它时,不会触发onChange 事件。我不知道为什么会发生这种情况

      【讨论】:

        最近更新 更多