【问题标题】:with redux-form and react-select.creatable why does a blur event clear the input?使用 redux-form 和 react-select.creatable 为什么模糊事件会清除输入?
【发布时间】:2024-01-22 08:19:02
【问题描述】:

我正在使用:

  • 反应 15.6.2
  • redux-form 7.2.0
  • 反应选择 1.2.1

我还没有为此设置代码笔,这很复杂。希望有人会单独从源头上看到问题。我可以做梦,不是吗?

我有一个 redux-form 显示一个 invoice 对象:

invoice = { client: { email: 'someguy@somewhere.com' } }

总结:我使用react-select.creatable 作为电子邮件下拉列表的自动完成组件。选择email 会在invoice 上设置client

当我创建一个新的电子邮件地址并选择它,然后模糊输入字段时,输入值被清除。

invoice 表单上的新 client 仍然在 redux 存储中正确设置,它只是从字段的输入值中清除。为什么?

  • 请注意,根据其他答案,我已经使用正确的值从 Select 手动调用 input.onBlur()。我认为这与创建新选项有关。

    // 包装自定义自动完成组件的 redux-form 字段

    const clientOptions = clients.map(client => ({ label: client.email, value: client })) const isValidNewOption = ({ label }) => email.REG_EMAIL.test(label) const promptTextCreator = (label) => (Send to ${label}) const newOptionCreator = ({label, labelKey, valueKey}) => { 返回{标签:标签,值:{电子邮件:标签}} }

// RFReactSelect,一个react-select 可创建作为自动完成功能,能够创建一个新选项

import React, { PropTypes } from 'react';
import Select from 'react-select';
import 'react-select/dist/react-select.css';

RFReactSelect.defaultProps = {
  multi: false,
  className: ""
};

RFReactSelect.propTypes = {
  input: PropTypes.shape({
    name: PropTypes.string.isRequired,

    onBlur: PropTypes.func.isRequired,
    onChange: PropTypes.func.isRequired,
    onFocus: PropTypes.func.isRequired,
  }).isRequired,
  options: PropTypes.array.isRequired,
  multi: PropTypes.bool,
  className: PropTypes.string,
  onNewOptionClick: PropTypes.func,
};

export default function RFReactSelect(props) {
  const { input , options, multi, className,
    newOptionCreator, promptTextCreator, isValidNewOption } = props
  const { name, value, onBlur, onChange, onFocus } = input;
  const transformedValue = transformValue(value, options, multi);

  return (
    <Select.Creatable
      className={className}
      isValidNewOption={isValidNewOption}
      name={name}
      multi={multi}
      newOptionCreator={newOptionCreator}
      onSelectResetsInput={false}
      onBlurResetsInput={false}
      options={options}
      onChange={multi
        ? multiChangeHandler(onChange)
        : singleChangeHandler(onChange)
      }
      onBlur={() => onBlur(value)}
      onFocus={onFocus}
      promptTextCreator={promptTextCreator}
      value={transformedValue}
      valueKey='value'
    />
  );
}

/**
 * onChange from Redux Form Field has to be called explicity.
 */
function singleChangeHandler(func) {
  return function handleSingleChange(value) {
    func(value ? value.value : '');
  };
}

/**
 * onBlur from Redux Form Field has to be called explicity.
 */
function multiChangeHandler(func) {
  return function handleMultiHandler(values) {
    func(values.map(value => value.value));
  };
}

/**
 * For single select, Redux Form keeps the value as a string, while React Select
 * wants the value in the form { value: "grape", label: "Grape" }
 *
 * * For multi select, Redux Form keeps the value as array of strings, while React Select
 * wants the array of values in the form [{ value: "grape", label: "Grape" }]
 */
function transformValue(value, options, multi) {
  if (multi && typeof value === 'string') return [];

  const filteredOptions = options.filter(option => {
    return multi
      ? value.indexOf(option.value) !== -1
      : option.value === value;
  });

  return multi ? filteredOptions : filteredOptions[0];
}

【问题讨论】:

    标签: reactjs autocomplete redux-form react-select


    【解决方案1】:

    我解决了。问题是您必须跟踪本地状态下新创建的选项,并将它们与组件的props.options 连接起来,否则新选项在重新渲染时会丢失。所以value 传回FieldonBlur 为空。这是来源:

    import React, { Component, PropTypes } from 'react';
    import Select from 'react-select';
    import 'react-select/dist/react-select.css';
    
    class RFReactSelect extends Component {
      constructor() {
        super()
    
        this.state = {
          createdOptions: []
        }
    
        this.onNewOptionClick = this.onNewOptionClick.bind(this)
      }
    
      render() {
        const { input , options, multi, className,
          newOptionCreator, promptTextCreator, isValidNewOption } = this.props
        const { name, value, onBlur, onChange, onFocus } = input;
        const allOptions = options.concat(this.state.createdOptions)
        const transformedValue = this.transformValue(value, allOptions, multi);
    
        return (
          <Select.Creatable
            className={className}
            isValidNewOption={isValidNewOption}
            multi={multi}
            name={name}
            newOptionCreator={newOptionCreator}
            onSelectResetsInput={false}
            onBlurResetsInput={false}
            options={allOptions}
            onChange={multi
              ? this.multiChangeHandler(onChange)
              : this.singleChangeHandler(onChange)
            }
            onBlur={() => onBlur(value)}
            onFocus={onFocus}
            onNewOptionClick={this.onNewOptionClick}
            promptTextCreator={promptTextCreator}
            ref='creatable'
            value={transformedValue}
            valueKey='value'
          />
        );
      }
    
      /**
       * Keep created options in local state or they will be lost
       * on re-render
       */
      onNewOptionClick(option) {
        const { props, select } = this.refs.creatable
        const { options } = props
    
        options.unshift(option)
        select.selectValue(option)
    
        this.setState({
          createdOptions: [option]
        })
      }
    
      /**
       * onChange from Redux Form Field has to be called explicity.
       */
      singleChangeHandler(func) {
        return function handleSingleChange(option) {
          func(option ? option.value : '');
        };
      }
    
      /**
       * onBlur from Redux Form Field has to be called explicity.
       */
      multiChangeHandler(func) {
        return function handleMultiHandler(values) {
          func(values.map(value => value.value));
        };
      }
    
      /**
       * For single select, Redux Form keeps the value as a string, while React Select
       * wants the value in the form { value: "grape", label: "Grape" }
       *
       * * For multi select, Redux Form keeps the value as array of strings, while React Select
       * wants the array of values in the form [{ value: "grape", label: "Grape" }]
       */
      transformValue(value, options, multi) {
        if (multi && typeof value === 'string') return [];
    
        const filteredOptions = options.filter(option => {
          return multi
            ? value.indexOf(option.value) !== -1
            : option.value === value;
        });
    
        return multi ? filteredOptions : filteredOptions[0];
      }
    }
    
    RFReactSelect.defaultProps = {
      multi: false,
      className: ""
    };
    
    RFReactSelect.propTypes = {
      input: PropTypes.shape({
        name: PropTypes.string.isRequired,
        onBlur: PropTypes.func.isRequired,
        onChange: PropTypes.func.isRequired,
        onFocus: PropTypes.func.isRequired,
      }).isRequired,
      options: PropTypes.array.isRequired,
      multi: PropTypes.bool,
      className: PropTypes.string,
      onNewOptionClick: PropTypes.func,
    }
    
    export default RFReactSelect
    

    【讨论】:

    • 你能为这个例子创建一个沙箱吗?