Множественный выбор в раскрывающемся списке в React - PullRequest
0 голосов
/ 19 июня 2020

Я пытаюсь выбрать несколько разных значений из созданного мной раскрывающегося списка. Итак, следующий код на самом деле показывает имена людей, но я хотел бы иметь возможность выбрать более одного.

<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>

Ответы [ 3 ]

0 голосов
/ 21 июня 2020

(я предполагаю, что вы используете react-bootstrap)

Ответ

Вам нужно будет передать другую опору в Form.Control, чтобы указать, что вы хотите, чтобы ваш выбор разрешил несколько выбор. Вы можете сделать это либо как multiple={true}, либо как сокращенное обозначение multiple (оба эквивалентны).

В этом примере в своих документах используется множественный выбор, что может быть полезно.

Я собрал эту песочницу , которая может проиллюстрировать, как ее использовать.

Что сложно

Обработка состояния с помощью react может быть сложной. Формы известны своей сложностью, потому что они включают в себя большое количество внутренних состояний.

Другие вещи, которые стоит попробовать

  • Используйте перехватчики реакции для управления состоянием вместо компонента класса. Это может упростить работу с состоянием, на мой взгляд

Ошибка в моем примере

  • Когда есть только один выбор, и вы пытаетесь отменить его, onChange событие не запускается. Я не уверен, почему это происходит здесь
0 голосов
/ 21 июня 2020

Событие необходимо добавить в индивидуальную опцию, для реализации множественного выбора требуется довольно много строк. Вот несколько фрагментов, которые могут вас заинтересовать. Как видите, я не использую сторонние элементы управления.

      <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>
      ))}

onSelect и onClear могут быть предоставлены из родительского / собственного компонента,

  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
0 голосов
/ 19 июня 2020

Это похоже на установку одного значения, но вместо этого значение представляет собой массив, а не строку или число.

Во-первых, вы должны изменить то, что делают значение и функция onChange. В качестве значения установите состояние по умолчанию в виде массива. Для onChange мы собираемся установить его там, где всякий раз, когда элемент отмечен, он устанавливает новое состояние, например:

javascript

state = {
 selectedNames:[]
}

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

Надеюсь, это поможет!

...