如何在 semantic-ui-react 中创建完全受控的下拉菜单
How to create fully controlled dropdown in 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 个问题:
- 没有出现初始值,我深入研究了代码,发现如果我不通过
options
属性 它不会找到给定的值,因此,它不会被显示。我可以使用 text
属性,但它似乎是一个 hack。
- 需要自己实现
handleItemClick
,看到original handleItemClick里面有逻辑。
有什么建议吗?我在这里错过了什么吗?
要解决第一个问题,请删除 clearable={true}
和 text={tagOptions[1].value}
handleItemClick
函数应该做什么?
我已经能够通过在下拉菜单上使用 ref 并传递原始 handleItemClick
方法来破解它。
目前唯一的缺点是键盘导航不起作用:\
它似乎不是为完全控制而设计的。
下拉模块根本不支持控制其内部组件,据说这是我所获得的最接近 react-window 支持的受控下拉菜单。我将它发布在这里,供将来想要 select 虚拟化下拉菜单而不头疼的任何人使用。
VirtualisedDropdown.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
VirtualisedDropdown.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;
}
}
我想创建一个完全受控的下拉菜单,以便使用 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 个问题:
- 没有出现初始值,我深入研究了代码,发现如果我不通过
options
属性 它不会找到给定的值,因此,它不会被显示。我可以使用text
属性,但它似乎是一个 hack。 - 需要自己实现
handleItemClick
,看到original handleItemClick里面有逻辑。
有什么建议吗?我在这里错过了什么吗?
要解决第一个问题,请删除
clearable={true}
和text={tagOptions[1].value}
handleItemClick
函数应该做什么?
我已经能够通过在下拉菜单上使用 ref 并传递原始 handleItemClick
方法来破解它。
目前唯一的缺点是键盘导航不起作用:\
它似乎不是为完全控制而设计的。
下拉模块根本不支持控制其内部组件,据说这是我所获得的最接近 react-window 支持的受控下拉菜单。我将它发布在这里,供将来想要 select 虚拟化下拉菜单而不头疼的任何人使用。
VirtualisedDropdown.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
VirtualisedDropdown.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;
}
}