如何使用带有重置按钮的 Formik 重置 react-select 单输入

How to reset a react-select single-input with Formik with reset button

我是 React 的新手,尤其是 react-select 和 Formik。我有一个包含 Input 组件和三个 Select 组件的表单。当我只使用基本的 reset 按钮时,输入以及使用 isMulti 用于多个 selected 选项的两个 selects 立即清除就好了,但是单个select 组件没有。如果我检查 values 是什么,它们是空的,但 UI 不反映此更改。我试过:

利用 resetForm(),将其设置为 initialValues 以及一个空对象。

使用 onReset 并从那里隐式调用 resetForm

使用 setFieldValue

的几种不同变体

我认为这可能是我的 initialValues 的设置方式,但在这一点上我只是在兜圈子,希望更老练的眼光能够理解这一点。

(PS- 文档中的示例向您展示了如何将 React-Select 与带有重置按钮的 Formik 一起使用,但它没有给出非多 select.)

单个 select 的名称为 'paid',我使用值和标签包含了我认为正确的对象 属性

简化的沙箱。期望的行为:单击 'reset' 会将选项重置为 initialValues 并在 UI.

中显示占位符文本

https://codesandbox.io/s/peh1q

const costOptions = [
   { value: 'true', label: 'Paid' },
   { value: 'false', label: 'Free' },
];

Resources.propTypes = {
  initialValues: shape({
    category: array,
    q: string,
    languages: array,
    paid: string,
  }),
};

Resources.defaultProps = {
  initialValues: {
    category: [],
    q: '',
    languages: [],
    paid: '',
  },
};

       <Formik
          enableReinitialize
          initialValues={initialValues}
          onSubmit={(values, actions) => {
            handleSubmit(values, actions);
            actions.setSubmitting(true);
          }}
        >
          {({ isSubmitting }) => (
            <Form>
              <Field
                data-testid={RESOURCE_SEARCH}
                disabled={isSubmitting}
                type="search"
                name="q"
                label="Search Keywords"
                component={Input}
              />
              <div className={styles.formContainer}>
                <div className={styles.selectColumn}>
                  <Field
                    isDisabled={isSubmitting}
                    isMulti
                    placeholder="Start typing a category..."
                    label="By Category"
                    name="category"
                    options={allCategories}
                    component={Select}
                  />
                </div>

                <div className={styles.selectColumn}>
                  <Field
                    isDisabled={isSubmitting}
                    placeholder="Resource cost..."
                    label="By Cost"
                    name="paid"
                    options={costOptions}
                    component={Select}
                  />
                </div>

                <div className={styles.selectColumn}>
                  <Field
                    isDisabled={isSubmitting}
                    placeholder="Start typing a language..."
                    isMulti
                    label="By Language(s)"
                    name="languages"
                    options={allLanguages}
                    component={Select}
                  />
                </div>
              </div>
              <div className={styles.buttonGroup}>
                <Button disabled={isSubmitting} type="submit">
                  Search
                </Button>

                <Button disabled={isSubmitting} type="reset">
                  Reset
                </Button>
              </div>
            </Form>
          )}
        </Formik>

您是否尝试过将 isClearable 属性添加到单值下拉列表中?

        <Formik
          enableReinitialize
          initialValues={initialValues}
          onSubmit={(values, actions) => {
            handleSubmit(values, actions);
            actions.setSubmitting(true);
          }}
        >
          {({ isSubmitting }) => (
            <Form>
              //...Other Formik components

                <div className={styles.selectColumn}>
                  <Field
                    isClearable={true} // <-- added this
                    isDisabled={isSubmitting}
                    placeholder="Resource cost..."
                    label="By Cost"
                    name="paid"
                    options={costOptions}
                    component={Select}
                  />
                </div>

                <div className={styles.selectColumn}>
                  <Field
                    isDisabled={isSubmitting}
                    placeholder="Start typing a language..."
                    isMulti
                    label="By Language(s)"
                    name="languages"
                    options={allLanguages}
                    component={Select}
                  />
                </div>
              </div>
              <div className={styles.buttonGroup}>
                <Button disabled={isSubmitting} type="submit">
                  Search
                </Button>

                <Button disabled={isSubmitting} type="reset">
                  Reset
                </Button>
              </div>
            </Form>
          )}
        </Formik>

所以我需要修复的东西实际上都在我发布的代码中(学习点),但它在 codesandbox 中。

Select 在 Formik 中使用的组件如下所示:

import React from 'react';
import {
  arrayOf,
  bool,
  func,
  number,
  object,
  objectOf,
  oneOfType,
  shape,
  string,
} from 'prop-types';
import { ErrorMessage } from 'formik';
import Alert from 'components/Alert/Alert';
import Label from 'components/Form/Label/Label';
import ThemedReactSelect from './ThemedReactSelect';
import styles from './Select.module.css';

Select.propTypes = {
  field: shape({
    name: string.isRequired,
    value: oneOfType([string.isRequired, arrayOf(string.isRequired).isRequired]),
  }).isRequired,
  form: shape({
    // TODO: Resolve why multiselects can end up with touched: { key: array }
    // see ThemedReactSelect as well
    // touched: objectOf(bool).isRequired,
    touched: object.isRequired,
    errors: objectOf(string).isRequired,
    setFieldTouched: func.isRequired,
    setFieldValue: func.isRequired,
  }).isRequired,
  hasValidationStyling: bool,
  id: oneOfType([string, number]),
  isLabelHidden: bool,
  isMulti: bool,
  label: string.isRequired,
  options: arrayOf(shape({ label: string.isRequired, value: string.isRequired }).isRequired)
    .isRequired,
};

Select.defaultProps = {
  hasValidationStyling: true,
  id: undefined,
  isLabelHidden: false,
  isMulti: false,
};

export default function Select({
  field: { name, value: fieldValue },
  form: { errors, setFieldTouched, setFieldValue, touched },
  hasValidationStyling,
  id,
  isLabelHidden,
  isMulti,
  label,
  options,
  ...props // disabled, placeholder, etc.
}) {
  /**
   * @description handle changing of non-multi select
   * @param {string} selected
   */
  const onChangeSingle = selected => {
    setFieldValue(name, selected.value);
  };

  /**
   * @description handle changing of multi select
   * @param {string[]} selectedArray
   */
  const onChangeMulti = selectedArray => {
    if (selectedArray) {
      setFieldValue(
        name,
        selectedArray.map(item => item.value),
      );
    } else {
      setFieldValue(name, []);
    }
  };

  /**
   * @description Return the selected value as a string
   * @returns {string}
   */
  const getValueFromSingle = () => {
    return options.find(option => option.value === fieldValue);
  };

  /**
   * @description Return an array of selected values for multi selects
   * @returns {string[]}
   */
  const getValueFromMulti = () => {
    return options.filter(option => fieldValue.includes(option.value));
  };

  const handleBlur = () => {
    setFieldTouched(name);
  };

  const hasErrors = Boolean(errors[name]);

  // handlers and value depend on whether or not select allows for multiple selections.
  const value = isMulti ? getValueFromMulti() : getValueFromSingle();
  const onChangeHandler = isMulti ? onChangeMulti : onChangeSingle;

  return (
    <div className={styles.field}>
      <Label for={name} isHidden={isLabelHidden}>
        {label}
      </Label>

      <div className={styles.selectFeedbackGrouping}>
        <ThemedReactSelect
          {...props}
          hasErrors={hasErrors}
          hasValidationStyling={hasValidationStyling}
          isTouched={touched[name]}
          id={id || name}
          isMulti={isMulti}
          name={name}
          onBlur={handleBlur}
          onChange={onChangeHandler}
          options={options}
          value={value}
        />

        <ErrorMessage
          name={name}
          render={message => {
            return hasErrors ? (
              <Alert className={styles.errorMessage} type="error">
                {message}
              </Alert>
            ) : null;
          }}
        />
      </div>
    </div>
  );
}

首先,当输入有 isClearable={true} 时,处理传入 handleOnChange 的空值,然后单击 'X' 清除 select

 const onChangeSingle = selected => {
    setFieldValue(name, selected === null ? '' : selected.value);
  };

然后,给字段值一个fallback(在上面的ThemedReactSelect中)

<ThemedReactSelect
          {...props}
          hasErrors={hasErrors}
          hasValidationStyling={hasValidationStyling}
          isTouched={touched[name]}
          id={id || name}
          isMulti={isMulti}
          name={name}
          onBlur={handleBlur}
          onChange={onChangeHandler}
          options={options}
          value={value || ''}
        />

现在单个 select 的工作方式与重置表单时的多个一样。