Reputation: 1709
I'm working with a react form validation using Yup
along with Formik
.
There is a react-select
element in the form which needs to be validated as well. For validation i'm making use of validationSchema
of Formik
to validate form on value change.
I need only value of the select field as a string so cant take the complete object (key-value).
The select field is working fine how ever the validation error message is not cleared.
The question is how can I validate the select field with existing approach?
Below is the minimal code sample.
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);
Here is the codesandbox :
PS: I'm new to Reactjs.
Upvotes: 18
Views: 41724
Reputation: 125
The setFieldValue
can be used here. Refer this code sample below:
<Select
placeholder="Year"
value={selectedYear}
onChange={selectedOption => {
setFieldValue(selectedOption);
}}
isSearchable={true}
options={yearOptions}
name="year"
isLoading={false}
loadingMessage={() => "Fetching year"}
noOptionsMessage={() => "Year appears here"}
/>
Upvotes: 0
Reputation: 743
Just destructe the setFieldValue from Formik and then set the value in select of option. Here is the example:
<Select
withAsterisk
label="Select Name"
placeholder="Select State"
data={['abc', 'xyz', 'asd', 'vbn']}
searchable
nothingFoundMessage="No State Found..."
name="your_fileld_name_here"
value={values.state_select}
onChange={(value) => **setFieldValue('your_fileld_name_here', value)}**
onBlur={handleBlur}
/>
Upvotes: 0
Reputation: 261
You need to fool Formik into considering this as an event. handleChange
here is from Formik. This also works for any other input field type such as react-color
, or datepicker.
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}
/>
Upvotes: 11
Reputation: 1
In case someone is still looking for an asnwer. This will help.
Initially create a custom component using Select
component.
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>
);
}
Import the custom component and use as follows. Make sure to use setFieldTouched
and setFieldValue
from formik props, to set and validate the form.
<CustomSelect
name="name"
value={"value"}
options={options}
onChange={(option) => {
setFieldTouched("name", true);
setFieldValue("name", option.value);
}}
/>
Upvotes: 0
Reputation: 5410
I solve my problem by using following code.
import this at the top
import Select from 'react-select';
import { Formik, Form, Field } from 'formik';
now write this code at jsx render part
<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>
its only the example for use react-select
with formik
and update the value in formik validation
you can also use useFormik
hook for the same but this is the different way
Upvotes: 2
Reputation: 19
I would use Formik select instead of react select like this:
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>
use map on options array to make options
Upvotes: 1
Reputation: 931
In case somebody is looking for solutions to this in the future, here's an alternative approach to get the selected value as a string from react-select -- and still have Yup perform the validation on the select input:
Using the helpers function from useField(), you can set the value, touched, and error state of a "Field". useField() is helpful any time you're working with elements that aren't inputs, like 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)} />
);
};
Upvotes: 3
Reputation: 18759
Change
handleChange("year")
To
handleChange("year")(selectedOption.value);
Currently the year
field in the Formik value isn't updated. The handleChange() function returns a new function that can be called with a value to update the Formik state.
Easiest way to spot these things is by outputting the Formik props with the following code:
<pre>{JSON.stringify(props, null, 2)}</pre>
See this sandbox for an example. In the sandbox I have completely removed the need for the custom year state. I'd recommend using only the Formik state to manipulate the values. Using only Formik state you will probably have to extract only the year part when saving, because react-select uses the complete object by default.
Upvotes: 16