使用 Formik 与 Yup 和 React-select 进行验证

Validation using Formik with Yup and React-select

我正在使用 YupFormik 进行 React 表单验证。 表单中还有一个 react-select 元素也需要验证。为了进行验证,我使用 validationSchema of Formik 来验证值更改时的表单。 我只需要 select 字段的值作为字符串,因此不能获取完整的对象(键值)。 select 字段工作正常,但未清除验证错误消息。 问题是如何使用现有方法验证 select 字段?

下面是最小的代码示例。

import ReactDOM from "react-dom";
import React, { useState } from "react";
import { Grid, TextField, Button } from "@material-ui/core";
import { Formik } from "formik";
import * as Yup from "yup";
import Select from "react-select";
import "./styles.css";

function App() {
  const [selectedYear, setSelectedYear] = useState("");

  const testSchema = Yup.object().shape({
    name: Yup.string().required("Enter Name"),
    year: Yup.string().required("Select Year")
  });

  const initialValues = {
    name: "",
    year: ""
  };

  const handleYearChange = (selectedYear, values) => {
    values.year = selectedYear.value;
    console.log(selectedYear);
    setSelectedYear(selectedYear);
  };

  const yearOptions = [
    { value: "1960", label: "1960" },
    { value: "1961", label: "1961" },
    { value: "1962", label: "1962" },
    { value: "1963", label: "1963" },
    { value: "1964", label: "1964" },
    { value: "1965", label: "1965" }
  ];

  return (
    <Formik validationSchema={testSchema} initialValues={initialValues}>
      {({
        handleChange,
        handleBlur,
        values,
        errors,
        touched,
        handleSubmit,
        setFieldTouched
      }) => {
        return (
          <>
            <Grid container spacing={2}>
              <Grid item md={12} xs={12}>
                <TextField
                  label="Name"
                  name="name"
                  margin="normal"
                  variant="outlined"
                  onChange={handleChange("name")}
                  style={{ width: "100%", zIndex: 0 }}
                  value={values.name}
                  onBlur={() => {
                    console.log("name");
                  }}
                />
                {errors.name}
              </Grid>

              <Grid item md={6} xs={12}>
                <Select
                  placeholder="Year"
                  value={selectedYear}
                  onChange={selectedOption => {
                    handleYearChange(selectedOption);
                    // handleYearChange(selectedOption, values);
                    // values.year = selectedOption.value;
                    console.log("values", values.year);
                    handleChange("year");
                  }}
                  isSearchable={true}
                  options={yearOptions}
                  name="year"
                  isLoading={false}
                  loadingMessage={() => "Fetching year"}
                  noOptionsMessage={() => "Year appears here"}
                />
                {errors.year}
              </Grid>
              <Grid
                item
                md={4}
                style={{ marginTop: "24px", marginBottom: "10px" }}
                xs={12}
              >
                <Button onClick={handleSubmit}>Save</Button>
              </Grid>
            </Grid>
          </>
        );
      }}
    </Formik>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

这是代码框:

PS: 我是 Reactjs 的新手。

改变

handleChange("year")

handleChange("year")(selectedOption.value);

目前 Formik 值中的 year 字段未更新。 handleChange() 函数 returns 一个新函数,可以用一个值调用来更新 Formik 状态。

发现这些东西的最简单方法是使用以下代码输出 Formik 道具:

<pre>{JSON.stringify(props, null, 2)}</pre>

See this sandbox 举个例子。在沙盒中,我已经完全消除了对自定义年份状态的需求。我建议仅使用 Formik 状态来操纵这些值。仅使用 Formik 状态,您在保存时可能只需要提取年份部分,因为 react-select 默认使用完整对象。

以防将来有人正在寻找解决方案,这里有另一种方法可以从 react-select 中获取 selected 值作为字符串——并且仍然让 Yup 执行select 输入的验证:

使用 useField() 的辅助函数,您可以设置 "Field" 的值、触摸和错误状态。 useField() 在您处理非输入元素时很有用,例如 react-select.

function FormikSelect(...props) {
  const [field, meta, helpers] = useField(name="mySelectInput"); // can pass 'props' into useField also, if 'props' contains a name attribute
  const { setValue, setTouched, setError } = helpers;

  const setFieldProps = (selectedOption) => {
      setValue(selectedOption.value) 
      setTouched(true)
      setError(undefined)
  }

  return (
        <Select onChange={selectedOption => setFieldProps(selectedOption)} />
  );
};

简单的解决方案 :: 它有效,,,,,甚至对于 任何其他输入类型 的输入字段,如 react-color, 或日期选择器或任何东西... 通过使用此逻辑.. 你需要愚弄 formik,它会将此视为事件,,示例如下, ,这里 - handleChange 来自 formik

validationSchema = yup.object({
     year_value :yup.object().required('*year value is required.')
})

<Select 
    className="  "
    name="year_value"
    id="year_value"
    placeholder='Choose year value'
    value={values.year_value}
    onBlur={handleBlur}
    onChange={selectedOption => {
        let event = { target : { name:'year_value',value: selectedOption}}
        handleChange(event)
    }}
    onBlur={()=>{
      handleBlur({ target: {name:'year_value'} });
    }}
    options={yearOptions}
/>

我会使用 Formik select 而不是像这样反应 select:

    const initialValues = {
        name: "",
        year: ""
    };

    const testSchema = Yup.object().shape({
      name: Yup.string().required("Enter Name"),
      year: Yup.string().required("Select Year")
    });

    <Field as="select" name="year" id="year">
    <option value="" label="">
    Select Your Year{" "}
    </option>
    {yearOptions.map(item => 
       <option value={item.value} label={item.label}>{item.value}</option>
   )} 
    </Field>

在选项数组上使用映射来制作选项

我使用以下代码解决了我的问题。

在顶部导入这个

import Select from 'react-select';
import { Formik, Form, Field } from 'formik';

现在在 jsx 渲染部分编写这段代码

<Formik
      initialValues={initialUserAddData}
      validationSchema={addUserValidationSchema}
      onSubmit={handleAddUser}
    >
      {({ errors }) => (
        <Form className="add-edit-user-form">
          <Field name="department">
            {({ field, form }) => (
               <Select
                 className="select-wrap"
                 classNamePrefix="select-box"
                 options={department}
                 onChange={(selectedOption) =>
                     form.setFieldValue(
                       'department',
                       selectedOption.value,
                      )
                    }
               />
            )}
         </Field>
         {errors.department && (
           <span className="error">{errors.department}</span>
         )}
       </Form>
      )}
 </Formik>

它只是将 react-selectformik 一起使用并更新 formik 验证中的值的示例 您也可以使用 useFormik 挂钩,但这是不同的方式

以防有人还在寻找答案。这会有所帮助。

最初使用 Select 组件创建自定义组件。

import Select from "react-select";

export default function CustomSelect({ onChange, options, value, name, className = "" }) {
    const defaultValue = (options, value) => {
        return options ? options.find((option) => option.value === value) : "";
    };

    return (
        <div>
            <Select
                className={className}
                name={name}
                value={defaultValue(options, value)}
                onChange={(value) => {
                    onChange(value);
                }}
                options={options}
            />
        </div>
    );
}

导入自定义组件,使用如下。确保使用 formik 道具中的 setFieldTouchedsetFieldValue 来设置和验证表单。

<CustomSelect
   name="name"
   value={"value"}
   options={options}
   onChange={(option) => {
   setFieldTouched("name", true);
   setFieldValue("name", option.value);
   }}
/>