React-select 在保留过滤器的同时清除值

React-select clear value while keeping filter

我正在开发一个权限系统,让用户可以控制谁可以 access/comment/edit 资源,就像您可以在 Google Drive 上找到的一样。我正在使用 React-Select multi 让资源的所有者选择他想要授予资源访问权限的用户。

当我点击 react-select 显示的选项时,我希望将此选项添加到我的允许用户列表(由另一个组件处理的列表)。这部分有效,我只是在 select 上使用了一个 onChange 处理程序(正如您在下面的代码中看到的那样)。

export default class AddUsersForm extends Component {

  PropTypes = {
    onSubmit: PropTypes.func.isRequired,
    usersList: PropTypes.array.isRequired, // List of all users in the company
    usersInPermissions: PropTypes.array.isRequired, // Users who already have access to the resource
  }

  handleChange(users){
    // Adds new user to the list of allowed users, an updated value for this.props.usersInPermissions will be received
    this.props.onSubmit(users);
  }

  render() {
    return (
      <form>
        <Select
          name="users"
          multi={true}
          options={this.props.usersList.filter(user => !this.props.usersInPermissions.includes(user.id))}
          onChange={(users) => this.handleChange(users)}
        />
      </form>
    );
  }

}

这就是我卡住的地方:添加选项后,我想继续显示用户在文本字段中搜索第一个选项时可能使用的过滤器。它现在的工作方式是删除过滤器,所有选项都显示在下拉列表中。

是否有任何简单的方法可以使用 React-Select 实现此目的?

非常感谢!

此代码有效。也许有更好的方法。

// ManageUsers
import React, { PropTypes } from 'react';
import AddUserForm from './AddUserForm'

export default class NoMatch extends React.Component {
  constructor(props) {
    super(props)
    this.handleChange = this.handleChange.bind(this);

    let selectedUsers = [ { value: 3, label: 'Three' },
      { value: 4, label: 'Four' } ];

    this.state = {
      selectedUsers: selectedUsers
    }
  }

  handleChange(selected) {
    this.setState({ selectedUsers: selected })
  }

  render() {
    let usersList = [
      { value: 1, label: 'One' },
      { value: 2, label: 'Two' }
    ];

    return (
      <div>Users
        <AddUserForm usersList={usersList} 
         selectedUsers={this.state.selectedUsers} 
         handleChange={this.handleChange} />
      </div>
    );
  }
}
// AddUsersForm
import React, { PropTypes } from 'react';
import Select from 'react-select';
import 'react-select/dist/react-select.css';

export default class AddUsersForm extends React.Component {
 PropTypes = {
  usersList: PropTypes.array.isRequired,
  selectedUsers: PropTypes.array.isRequired,
  handleChange: PropTypes.func.isRequired
 }

 render() {
  return (
   <form>
    <Select
     multi={true}
     options={this.props.usersList}
     value={this.props.selectedUsers}
     onChange={(users) => this.props.handleChange(users)}
    />
   </form>
  );
 }
}

如果您想保留键入的文本,则必须在 handleChange 上设置输入文本。没有内置函数来保留键入的文本。

 onChange={(users) => this.props.handleChange(users, event)}
handleChange(selected, event) {
let selectedFilter = event.target;
 // then navigated to the input element with Javascript or jQuery
 // and set the value of the input

this.setState({ selectedUsers: selected })
}

我的方式:

  1. 用自己的组件替换了 Option 组件(Material-UI 库中的几个组件)。
  2. 覆盖 onClick 事件处理程序 - 这里是一些逻辑并从 ReactSelect 道具调用 onChange 处理程序。在onClick处理程序的末尾添加了event.stopPropagation()
import React from 'react';

import MenuItem from '@material-ui/core/MenuItem/MenuItem';
import Checkbox from '@material-ui/core/Checkbox/Checkbox';
import ListItemText from '@material-ui/core/ListItemText/ListItemText';

const MultiOption = props => (
  <MenuItem
    buttonRef={props.innerRef}
    {...props.innerProps}
    onClick={event => {
      let values = [];
      if (props.isSelected) {
        values = props.selectProps.value.filter(
          item => item.value !== props.value,
        );
      } else {
        values = [props.data].concat(props.selectProps.value);
      }
      props.selectProps.onChange(values);
      event.stopPropagation();
    }}
    style={{
      overflow: 'initial',
      padding: 0,
    }}
  >
    <Checkbox
      checked={props.isSelected}
      style={{
        padding: 4,
      }}
    />
    <ListItemText
      primary={props.label}
      classes={{
        root: props.selectProps.classes.optionRoot,
      }}
    />
  </MenuItem>
);

export default MultiOption;
import React from 'react';
import PropTypes from 'prop-types';
import Select from 'react-select';

import { withStyles } from '@material-ui/core/styles';

import { getComponents } from './components';

import { styles, getSelectStyles } from './styles';

class Combobox extends React.Component {
  handleChange = () => value => {
    const { onChange } = this.props;
    onChange(value);
  };

  render() {
    const {
      classes,
      theme,
      options,
      label,
      rootStyle,
      value,
      error,
      isInner,
      isMulti,
      fullWidth,
      ...props
    } = this.props;

    return (
      <div className={classes.root} style={{ ...rootStyle }}>
        <Select
          {...props}
          isClearable
          classes={classes}
          styles={getSelectStyles({
            theme,
            fullWidth,
          })}
          options={options}
          menuPortalTarget={document.body}
          menuPlacement="auto"
          value={value || null}
          onChange={this.handleChange()}
          components={getComponents({
            isInner,
            isMulti,
          })}
          textFieldProps={{
            label,
            error: !!error,
            helperText: error,
            InputLabelProps: { shrink: true },
          }}
          isMulti={isMulti}
          hideSelectedOptions={!isMulti}
          closeMenuOnSelect={!isMulti}
          loadingMessage={() => 'Loading...'}
        />
      </div>
    );
  }
}

Combobox.propTypes = {
  options: PropTypes.arrayOf(PropTypes.shape({})),
  label: PropTypes.string,
  classes: PropTypes.shape({}).isRequired,
  theme: PropTypes.shape({}).isRequired,
  value: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.bool,
    PropTypes.arrayOf(PropTypes.any),
    PropTypes.shape({}),
  ]),
  error: PropTypes.string,
  onChange: PropTypes.func.isRequired,
  isInner: PropTypes.bool,
  isMulti: PropTypes.bool,
  fullWidth: PropTypes.bool,
};

Combobox.defaultProps = {
  options: [],
  label: '',
  value: null,
  error: '',
  isInner: false,
  isMulti: false,
  fullWidth: false,
};

export default withStyles(styles, { withTheme: true })(({ ...props }) => (
  <Combobox {...props} />
));
import Control from './Control';
import InnerControl from './InnerControl';

import InputComponent from './InputComponent';
import MenuList from './MenuList';

import Option from './Option';
import MultiOption from './MultiOption';

import SingleValue from './SingleValue';
import MultiValue from './MultiValue';

import NoOptionsMessage from './NoOptionsMessage';

import Placeholder from './Placeholder';

import ValueContainer from './ValueContainer';

const getComponents = ({ isInner, isMulti }) => ({
  Control: isInner ? InnerControl : Control,
  ...(isMulti && { MenuList }),
  MultiValue,
  NoOptionsMessage,
  Option: isMulti ? MultiOption : Option,
  Placeholder,
  SingleValue,
  ValueContainer,
});

export {
  Control,
  InnerControl,
  InputComponent,
  MenuList,
  Option,
  MultiOption,
  SingleValue,
  MultiValue,
  NoOptionsMessage,
  Placeholder,
  ValueContainer,
  getComponents,
};