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

问题描述 投票:0回答:7

我正在使用

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

下面是最小的代码示例。

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);

这是代码和框:

Edit throbbing-shadow-6f6yw

PS:我是 Reactjs 新手。

reactjs material-ui react-select formik yup
7个回答
16
投票

改变

handleChange("year")

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

目前 Formik 值中的

year
字段尚未更新。 handleChange() 函数返回一个新函数,可以使用值调用该函数来更新 Formik 状态。

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

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

请参阅此沙箱以获取示例。在沙箱中,我完全消除了对自定义年份状态的需要。我建议仅使用 Formik 状态来操纵值。仅使用 Formik 状态,您在保存时可能只需要提取年份部分,因为反应选择默认使用完整对象。


11
投票

你需要愚弄福米克,让他将这视为一个事件。

handleChange
这里来自福米克。这也适用于任何其他输入字段类型,例如
react-color
或日期选择器。

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}
/>


3
投票

如果将来有人正在寻找解决方案,这里有一种替代方法,可以从react-select中以字符串形式获取所选值——并且仍然让Yup对选择输入执行验证:

使用 useField() 中的辅助函数,您可以设置“Field”的值、触摸和错误状态。每当您使用非输入元素(例如react-select)时,useField() 都会很有帮助。

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)} />
  );
};

2
投票

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

在顶部导入它

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-select
formik
并更新 formik 验证中的值的示例 您也可以使用
useFormik
钩子进行相同的操作,但这是不同的方式


1
投票

我会使用 Formik 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>

使用选项数组上的地图来制作选项


0
投票

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

最初使用

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 道具中的

setFieldTouched
setFieldValue
来设置和验证表单。

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

0
投票

只需从 Formik 中销毁 setFieldValue,然后在选项的选择中设置值。这是例子:

<Select
                            withAsterisk
                            label="your_fileld_name_here"
                            placeholder="Select State"
                            data={['abc', 'xyz', 'asd', 'vbn']}
                            searchable
                            nothingFoundMessage="No State Found..."
                            name="state_select"
                            value={values.state_select}
                            onChange={(value) => **setFieldValue('your_fileld_name_here', value)}**
                            onBlur={handleBlur}
                        />
© www.soinside.com 2019 - 2024. All rights reserved.