如何使用 react-select 呈现 "N items selected" 而不是 N selected 项目的列表

How to render "N items selected" rather than the list of N selected items with react-select

我正在研究使用 react-select 作为城市选择器的选择器,用户可以在其中选择 1 个或多个城市来过滤一些数据。这是在我的页面中呈现的屏幕截图:

城市列表可能很大,如果一次选择大量城市,我不希望选择器超出其蓝色容器。这是我现在模拟时发生的情况:

我不太喜欢它!我能想到的一种替代方法是呈现“选择的 4 个城市”而不是整个列表。这将在页面上具有可预测的大小。

react-select如何做到这一点?

在我看来,我会覆盖 css 以固定大小。是css的事情。我将检查元素并进行更改。

让我们看看上面的例子。这是您需要调整的css:

.Select-control {
background-color: #fff;
border-color: #d9d9d9 #ccc #b3b3b3;
border-radius: 4px;
border: 1px solid #ccc;
color: #333;
cursor: default;
display: table;
border-spacing: 0;
border-collapse: separate;
height: 36px;
outline: none;
overflow: hidden;
position: relative;
width: 100%;

}

现在删除 display: table; 并添加适当的高度。

注意:此答案适用于 react-select v1。有关 v3 的解决方案,请参阅

正在渲染“N 项 selected”

这可以通过 valueRendererclassName 道具以及最少量的 CSS 来实现。

这里我正常显示前三个 select 离子,然后在 selected 超过 4 个项目时显示“N 项 selected”。显示 remove selection 图标 (×) 除了“N items selected”是没有意义的,所以我也删除了那个(CSS).

class App extends React.Component {
  state = {
    value: [],
  }
  className = () => {
    const baseClassName = 'my-react-select';
    
    if (this.state.value.length <= 3) {
      return baseClassName;
    }
    
    return `${baseClassName} ${baseClassName}--compact`;
  }
  handleChange = (value) => {
    this.setState({ value });
  }
  renderValue = (option) => {
    // The first three selections are rendered normally
    if (this.state.value.length <= 3) {
      return option.label;
    }

    // With more selections, render "N items selected".
    // Other than the first one are hidden in CSS.
    return <span>{this.state.value.length} items selected</span>;
  }
  render() {
    return (
      <Select
        className={this.className()}
        multi
        onChange={this.handleChange}
        options={[
          { value: 'zero',  label: 'Zero' },
          { value: 'one',   label: 'One' },
          { value: 'two',   label: 'Two' },
          { value: 'three', label: 'Three' },
          { value: 'four',  label: 'Four' },
          { value: 'five',  label: 'Five' },
          { value: 'six',   label: 'Six' },
          { value: 'seven', label: 'Seven' },
          { value: 'eight', label: 'Eight' },
          { value: 'nine',  label: 'Nine' },
        ]}
        value={this.state.value}
        valueRenderer={this.renderValue}
      />
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
.my-react-select {
  /* Custom styles */
}

.my-react-select--compact .Select-value:first-child {
  font-style: italic;
}
.my-react-select--compact .Select-value:first-child .Select-value-icon,
.my-react-select--compact .Select-value:nth-child(n+2) {
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<script src="https://unpkg.com/prop-types@15.5.10/prop-types.js"></script>
<script src="https://unpkg.com/classnames@2.2.5/index.js"></script>
<script src="https://unpkg.com/react-input-autosize@2.0.0/dist/react-input-autosize.js"></script>
<script src="https://unpkg.com/react-select@1.3.0/dist/react-select.js"></script>

<link rel="stylesheet" href="https://unpkg.com/react-select@1.3.0/dist/react-select.css">

<div id="root"></div>


替代方法

查看您的屏幕截图,看起来 space 最多显示四个 select 离子,而不会造成 selector 溢出。当超过 4 个城市 selected 时显示“N 项 selected”, 您可以正常显示前 3 个 selections 然后“+N 更多." 像这样:

  • 城市A
  • A市,B市
  • A市、B市、C市
  • A市、B市、C市,+1个
  • A市、B市、C市,还有2个
  • A市、B市、C市,还有3个
  • 等等

从用户体验的角度来看,我认为正常显示前 3 个左右 selection 很好。如果在第 4 个城市 selected.

之后,每个 selection 突然隐藏在文本“4 items selected”后面,这会让人感到困惑。

这个解决方案与第一个非常相似。 className 道具现在只是一个字符串。 renderValue 方法和 CSS select 或者有点不同。

class App extends React.Component {
  state = {
    value: [],
  }
  handleChange = (value) => {
    this.setState({ value });
  }
  renderValue = (option) => {
    // The first three values are rendered normally
    if (this.state.value.indexOf(option) < 3) {
      return option.label;
    }

    // Render the rest as "+ N more". 
    // Other than the first one are hidden in CSS.
    return <span>+ {this.state.value.length - 3} more</span>;
  }
  render() {
    return (
      <Select
        className='my-react-select'
        multi
        onChange={this.handleChange}
        options={[
          { value: 'zero',  label: 'Zero' },
          { value: 'one',   label: 'One' },
          { value: 'two',   label: 'Two' },
          { value: 'three', label: 'Three' },
          { value: 'four',  label: 'Four' },
          { value: 'five',  label: 'Five' },
          { value: 'six',   label: 'Six' },
          { value: 'seven', label: 'Seven' },
          { value: 'eight', label: 'Eight' },
          { value: 'nine',  label: 'Nine' },
        ]}
        value={this.state.value}
        valueRenderer={this.renderValue}
      />
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
/* If you change the amount of how many selections are shown normally,
 * be sure to adjust these selectors accordingly. */
.my-react-select .Select-value:nth-child(4) {
  font-style: italic;
}
.my-react-select .Select-value:nth-child(4) .Select-value-icon,
.my-react-select .Select-value:nth-child(n+5) {
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<script src="https://unpkg.com/prop-types@15.5.10/prop-types.js"></script>
<script src="https://unpkg.com/classnames@2.2.5/index.js"></script>
<script src="https://unpkg.com/react-input-autosize@2.0.0/dist/react-input-autosize.js"></script>
<script src="https://unpkg.com/react-select@1.3.0/dist/react-select.js"></script>

<link rel="stylesheet" href="https://unpkg.com/react-select@1.3.0/dist/react-select.css">

<div id="root"></div>


这是显示 select 离子的另一种方法:

  • 城市A
  • A市,B市
  • A市、B市、C市
  • A市、B市、C市、D市
  • A市、B市、C市,还有2个
  • A市、B市、C市,还有3个
  • 等等

从用户体验的角度来看,显示“+ 1 more”而不是显示值有点傻,所以在我看来这是最好的选择。

renderValue 方法再次有点不同。 CSS select or 现在有点丑陋和复杂,但它们有效。

class App extends React.Component {
  state = {
    value: [],
  }
  handleChange = (value) => {
    this.setState({ value });
  }
  renderValue = (option) => {
    // The first four values are rendered normally
    if (this.state.value.length <= 4) {
      return option.label;
    }

    // The first 3 values are rendered normally when
    // more than 4 selections have been made
    if (this.state.value.indexOf(option) < 3) {
      return option.label;
    }

    // Render the rest as "+ N more".
    // Other than the first one are hidden in CSS.
    return <span>+ {this.state.value.length - 3} more</span>;
  }
  render() {
    return (
      <Select
        className='my-react-select'
        multi
        onChange={this.handleChange}
        options={[
          { value: 'zero',  label: 'Zero' },
          { value: 'one',   label: 'One' },
          { value: 'two',   label: 'Two' },
          { value: 'three', label: 'Three' },
          { value: 'four',  label: 'Four' },
          { value: 'five',  label: 'Five' },
          { value: 'six',   label: 'Six' },
          { value: 'seven', label: 'Seven' },
          { value: 'eight', label: 'Eight' },
          { value: 'nine',  label: 'Nine' },
        ]}
        value={this.state.value}
        valueRenderer={this.renderValue}
      />
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
/* If you change the amount of how many selections are shown normally,
 * be sure to adjust these selectors accordingly. */
.my-react-select .Select-value:nth-child(4):not(:nth-last-child(2)) {
  font-style: italic;
}
.my-react-select .Select-value:nth-child(4):not(:nth-last-child(2)) .Select-value-icon,
.my-react-select .Select-value:nth-child(n+5) {
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<script src="https://unpkg.com/prop-types@15.5.10/prop-types.js"></script>
<script src="https://unpkg.com/classnames@2.2.5/index.js"></script>
<script src="https://unpkg.com/react-input-autosize@2.0.0/dist/react-input-autosize.js"></script>
<script src="https://unpkg.com/react-select@1.3.0/dist/react-select.js"></script>

<link rel="stylesheet" href="https://unpkg.com/react-select@1.3.0/dist/react-select.css">

<div id="root"></div>

这是我使用 react-select 3.x 更新的答案。不涉及 css。我用我的自定义 multi-values 消息覆盖 ValueContainer 的子级。在此之前,您需要导入以下内容:

import React from "react";
import Select, { components } from "react-select";

变体 1

显示一般消息:n items selected

const ValueContainer = ({ children, ...props }) => {
  let [values, input] = children;

  if (Array.isArray(values)) {
    const plural = values.length === 1 ? "" : "s";
    values = `${values.length} item${plural} selected`;
  }

  return (
    <components.ValueContainer {...props}>
      {values}
      {input}
    </components.ValueContainer>
  );
};

结果

变体 2

在显示通用消息之前显示一些命名项目:item1, item2, item3 and n others selected

const ValueContainer = ({ children, ...props }) => {
  let [values, input] = children;

  if (Array.isArray(values)) {
    const val = (i: number) => values[i].props.children;
    const { length } = values;

    switch (length) {
      case 1:
        values = `${val(0)} selected`;
        break;
      case 2:
        values = `${val(0)} and ${val(1)} selected`;
        break;
      case 3:
        values = `${val(0)}, ${val(1)} and ${val(2)} selected`;
        break;
      default:
        const plural = values.length === 3 + 1 ? "" : "s";
        const otherCount = length - 3;
        values = `${val(0)}, ${val(1)}, ${val(
          2
        )} and ${otherCount} other${plural} selected`;
        break;
    }
  }
  return (
    <components.ValueContainer {...props}>
      {values}
      {input}
    </components.ValueContainer>
  );
};

用法

<Select
  {...}
  isMulti
  hideSelectedOptions={false}
  components={{ ValueContainer }}
/>

现场演示