【问题标题】:React-Select custom input inside MenuList在 MenuList 中反应选择自定义输入
【发布时间】:2019-03-01 08:12:12
【问题描述】:

我正在尝试使用 React-Select 选项创建自定义选择。我希望我的搜索不在控制框中,而是在菜单中。 我试过这个:

import React from "react";
import Select, { components } from "react-select";
import { colourOptions, groupedOptions } from "./docs/data";


const MenuList = props => {
  return (
    <components.MenuList {...props}>
      <components.Input {...props} />;
      {props.selectProps.inputValue.length > 1 ? props.children : ""}
    </components.MenuList>
  );
};
export default () => (
  <Select
    defaultValue={colourOptions[1]}
    options={groupedOptions}
    components={{ MenuList }}
  />
);

问题是我收到一个错误提示

Uncaught Invariant Violation: input is a void element tag and must neither have children nor use dangerouslySetInnerHTML

我猜反应选择的components.Input 是在input 标签内渲染另一个div 或类似的东西。 有没有人知道如何做到这一点?

【问题讨论】:

标签: reactjs react-select


【解决方案1】:

您应该通过Advanced 部分的文档中的建议来启发自己:https://react-select.com/advanced

我在 CodeSandbox 中重新创建了一个 live example,以便您可以看到它的实际效果并进行操作。但主要思想是将原始的Select 元素嵌入到某个受控元素中,然后他们编辑您的Select 的样式,使其感觉就像一个单一的MenuList

class PopoutExample extends Component<*, State> {
  state = { isOpen: false, value: undefined };
  toggleOpen = () => {
    this.setState(state => ({ isOpen: !state.isOpen }));
  };
  onSelectChange = value => {
    this.toggleOpen();
    this.setState({ value });
  };
  render() {
    const { isOpen, value } = this.state;
    return (
      <Dropdown
        isOpen={isOpen}
        onClose={this.toggleOpen}
        target={
          <Button
            iconAfter={<ChevronDown />}
            onClick={this.toggleOpen}
            isSelected={isOpen}
          >
            {value ? `State: ${value.label}` : "Select a State"}
          </Button>
        }
      >
        <Select
          autoFocus
          backspaceRemovesValue={false}
          components={{ DropdownIndicator, IndicatorSeparator: null }}
          controlShouldRenderValue={false}
          hideSelectedOptions={false}
          isClearable={false}
          menuIsOpen
          onChange={this.onSelectChange}
          options={stateOptions}
          placeholder="Search..."
          styles={selectStyles}
          tabSelectsValue={false}
          value={value}
        />
      </Dropdown>
    );
  }
}

// styled components

const Menu = props => {
  const shadow = "hsla(218, 50%, 10%, 0.1)";
  return (
    <div
      css={{
        backgroundColor: "white",
        borderRadius: 4,
        boxShadow: `0 0 0 1px ${shadow}, 0 4px 11px ${shadow}`,
        marginTop: 8,
        position: "absolute",
        zIndex: 2
      }}
      {...props}
    />
  );
};
const Blanket = props => (
  <div
    css={{
      bottom: 0,
      left: 0,
      top: 0,
      right: 0,
      position: "fixed",
      zIndex: 1
    }}
    {...props}
  />
);
const Dropdown = ({ children, isOpen, target, onClose }) => (
  <div css={{ position: "relative" }}>
    {target}
    {isOpen ? <Menu>{children}</Menu> : null}
    {isOpen ? <Blanket onClick={onClose} /> : null}
  </div>
);
const Svg = p => (
  <svg
    width="24"
    height="24"
    viewBox="0 0 24 24"
    focusable="false"
    role="presentation"
    {...p}
  />
);
const DropdownIndicator = () => (
  <div css={{ color: colors.neutral20, height: 24, width: 32 }}>
    <Svg>
      <path
        d="M16.436 15.085l3.94 4.01a1 1 0 0 1-1.425 1.402l-3.938-4.006a7.5 7.5 0 1 1 1.423-1.406zM10.5 16a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11z"
        fill="currentColor"
        fillRule="evenodd"
      />
    </Svg>
  </div>
);
const ChevronDown = () => (
  <Svg style={{ marginRight: -6 }}>
    <path
      d="M8.292 10.293a1.009 1.009 0 0 0 0 1.419l2.939 2.965c.218.215.5.322.779.322s.556-.107.769-.322l2.93-2.955a1.01 1.01 0 0 0 0-1.419.987.987 0 0 0-1.406 0l-2.298 2.317-2.307-2.327a.99.99 0 0 0-1.406 0z"
      fill="currentColor"
      fillRule="evenodd"
    />
  </Svg>
);

【讨论】:

  • 我喜欢这个,我曾经做过类似的事情,对于 MultiSelect,如果你想要 react-select 的所有样式,它会有点问题,我希望有一个更简单的解决方案,但我猜它不存在。
  • 对于多选,您将不得不付出很多努力来使用Dropdown 元素来伪造Select。 React-select 可以非常强大且可自定义,但不是那么简单。
  • 基于此解决方案,我对其进行了一些调整:codesandbox.io/s/custom-react-select-2-rtf1l
【解决方案2】:

作为 MenuList 的一部分输入。

它的灵感来自https://codesandbox.io/embed/m75wlyx3oy 解决方案。

默认MenuListCustomMenuWithInput 交换,后者首先在下拉列表中显示选项。选项列表由{ props.children } 提供。最后一个选项是输入字段。

onFocus={onMenuInputFocus} 阻止关闭输入中输入的每个字母的下拉菜单。


const CustomMenuWithInput = ({ selectProps: { onMenuInputFocus }, ...props }) => {
  const [value, setValue] = useState('')

  return (
    <div>
      { props.children }
      <input
        id='add-option'
        label='Add option'
        placeholder='option'
        autoCorrect='off'
        autoComplete='off'
        spellCheck='false'
        type='text'
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onMouseDown={(e) => {
          e.stopPropagation()
          e.target.focus()
        }}
        onFocus={onMenuInputFocus}
      />
    </div>
  )
}

const SelectView = ({ onChange }) => {
  const [inputValue, setInputValue] = useState('')
  const [isFocused, setIsFocused] = useState(false)

  return (
    <Select
      id=''
      label={'List'}
      options={list}
      onChange={opt => {
        onChange(opt.value, opt)
        setIsFocused(false)
      }}
      onInputChange={setInputValue}
      onMenuInputFocus={() => setIsFocused(true)}
      inputValue={inputValue}
      placeholder='Select...'
      components={{
        MenuList: CustomMenuWithInput,
      }}
      isSearchable
      {...{
        menuIsOpen: isFocused || undefined,
        isFocused: isFocused || undefined
      }}
    />
  )
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-06
    • 2021-10-10
    • 1970-01-01
    • 2020-12-28
    • 1970-01-01
    • 2020-06-27
    相关资源
    最近更新 更多