Как создать полностью контролируемый выпадающий список в semantic-ui-react

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

Я проверил документы, и нет ни одного примера контролируемого раскрывающегося списка с указанным Dropdown.Item.

Вот как выглядит мой компонент:

<Dropdown
  placeholder="Filter Posts"
  clearable={true}
  search={true}
  onChange={this.handleChange}
  text={tagOptions[1].value}
  value={tagOptions[1].value}
  onSearchChange={this.handleChange}
>
  <Dropdown.Menu>
    {tagOptions.map(option => (
      <Dropdown.Item key={option.value} {...option} onClick={this.handleItemClick} />
    ))}
  </Dropdown.Menu>
</Dropdown>;

Я столкнулся с 2 проблемами:

  1. Начальное значение не появляется, я покопался в коде и увидел, что если я не передам свойство options, оно не найдет заданное значение, поэтому оно не будет показано. Я могу использовать свойство text, но это похоже на хак.
  2. Мне нужно реализовать handleItemClick самостоятельно, и я вижу, что в исходный дескрипторItemClick.

Какие-либо предложения? я что-то пропустил здесь?


person felixmosh    schedule 24.03.2019    source источник


Ответы (3)


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

Единственным недостатком на данный момент является то, что навигация с клавиатуры не работает :\

Похоже, он не был предназначен для полного контроля.

https://codesandbox.io/s/ql3q086l5q

person felixmosh    schedule 24.03.2019

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

VirtualizedDropdown.js

import React, { forwardRef, useCallback, useRef, useState } from "react"
import { Dropdown, Ref } from "semantic-ui-react"
import { FixedSizeList } from "react-window"
import "./VirtualisedDropdown.scss"

const SUI_DROPDOWN_MENU_HEIGHT = 300
const SUI_DROPDOWN_MENU_ITEM_HEIGHT = 37

const VirtualisedDropdown = ({
  options, value,
  ...restProps
}) => {
  const dropdownRef = useRef()
  const listRef = useRef()

  const [open, setOpen] = useState(false)

  const OuterDiv = useCallback(({ style, ...props }, ref) => {
    const { position, overflow, ...restStyle } = style
    return (
      <Ref innerRef={ref}>
        <Dropdown.Menu open={open} {...props} style={restStyle}>
          {props.children}
        </Dropdown.Menu>
      </Ref>
    )
  }, [open])

  const InnerDiv = useCallback(props => {
    return (
      <Dropdown.Menu className="inner" open={open} style={{ ...props.style, maxHeight: props.style.height }}>
        {props.children}
      </Dropdown.Menu>
    )
  }, [open])

  return (
    <Dropdown
      className="virtualised selection"
      onClose={() => setOpen(false)}
      onOpen={() => {
        setOpen(true)
        listRef.current.scrollToItem(options.findIndex(i => i.value === value))
      }}
      // This causes "Warning: Failed prop type: Prop `children` in `Dropdown` conflicts with props: `options`. They cannot be defined together, choose one or the other."
      // but is necessary for some logic to work e.g. the selected item text.
      options={options}
      ref={dropdownRef}
      selectOnNavigation={false}
      value={value}
      {...restProps}
    >
      <FixedSizeList
        height={options.length * SUI_DROPDOWN_MENU_ITEM_HEIGHT < SUI_DROPDOWN_MENU_HEIGHT ? options.length * SUI_DROPDOWN_MENU_ITEM_HEIGHT + 1 : SUI_DROPDOWN_MENU_HEIGHT}
        innerElementType={InnerDiv}
        itemCount={options.length}
        itemData={{
          options,
          handleClick: (_e, x) => dropdownRef.current.handleItemClick(_e, x),
          selectedIndex: options.findIndex(i => i.value === value),
        }}
        itemSize={SUI_DROPDOWN_MENU_ITEM_HEIGHT}
        outerElementType={forwardRef(OuterDiv)}
        ref={listRef}
      >
        {Row}
      </FixedSizeList>
    </Dropdown>
  )
}

const Row = ({ index, style, data }) => {
  const { options, handleClick, selectedIndex } = data
  const item = options[index]

  return (
    <Dropdown.Item
      active={index === selectedIndex}
      className="ellipsis"
      key={item.value}
      onClick={handleClick}
      selected={index === selectedIndex}
      style={style}
      title={item.text}
      {...item}
    />
  )
}

export default VirtualisedDropdown

VirtualizedDropdown.scss

.ui.dropdown.virtualised .menu {
  &.inner {
    margin: 0 -1px !important;
    left: 0;
    overflow: initial;
    border-radius: 0 !important;
    border: 0;
  }

  > .item {
    text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
  }
}
person Mike    schedule 29.09.2020

  1. Чтобы решить первую проблему, удалите clearable={true} и text={tagOptions[1].value}

  2. Что должна делать функция handleItemClick?

person James Delaney    schedule 24.03.2019
comment
Журнал Cansole tagOptions[1]. чтобы проверить, получили ли вы какое-либо значение. {console.log('tagOptions[1].value', tagOptions[1].value)} - person James Delaney; 24.03.2019
comment
Да, вы можете играть с этой песочницей codesandbox.io/s/ql3q086l5q - person felixmosh; 24.03.2019