Open aliocharyzhkov opened 4 years ago
@aliocharyzhkov Did you resolve this? Looks like it's become stale.
If not, I'd love to have someone either take a deeper look for a fix or allow a PR to get merged for this.
I have a nested array using Yup validation and this breaks typescripts ability to compile.
Compile error
const hasErrors = errors?.permutations && errors.permutations[groupIdx].sets[setIdx].length // Property 'sets' does not exist on type 'string | FormikErrors<PermutationGroup>'.
Render error
<ErrorMessage name={`permutations[${groupIdx}].sets[${setIdx}]`} /> // Objects are not a valid react child
@imjakechapman I've used a temporary workaround. The issue hasn't been resolved in the library yet, so I went ahead and created a PR. I hope it will facilitate fixing it.
@aliocharyzhkov Can you plz share the workaround?
@hassaans I use a ternary operator to check if there's an error, and if there is I cast the object to the correct type. For example, errors.memebers[i] ? (errors.members[i] as IMember).name : ''
. Fortunately, there are just a few places where I have to use this hack. Moreover, I added a comment to remind me to refactor those line when the issue is fixed in the library.
I just ran into this problem yesterday and it was a huge pain. Why is this labeled stale?
@hassaans I use a ternary operator to check if there's an error, and if there is I cast the object to the correct type. For example,
errors.memebers[i] ? (errors.members[i] as IMember).name : ''
. Fortunately, there are just a few places where I have to use this hack. Moreover, I added a comment to remind me to refactor those line when the issue is fixed in the library.
I appreciate you providing the workaround. really hoping this gets fixed in the near future.
@hassaans I use a ternary operator to check if there's an error, and if there is I cast the object to the correct type. For example,
errors.memebers[i] ? (errors.members[i] as IMember).name : ''
. Fortunately, there are just a few places where I have to use this hack. Moreover, I added a comment to remind me to refactor those line when the issue is fixed in the library.
Thank you. I needed to modify it a bit to get rid of possible undefined
.
errors.members && errors.memebers[i] ? (errors.members[i] as IMember).name : ''
You can track errors anywhere inside of an object or array tree. For example, if I had a field called name in the shape of an object with name.first and name.last, I can
setFieldError("name", "please enter a first or last name.")
You can determine let typescript determine whether your error is a string or not with:
const error = typeof errors.yourObject === 'string'
? errors.yourObject
: errors.yourObject.yourSubValue
Or various other types of type guarding. (isObject, Array.isArray), Unless I'm mistaken, this is working as intended.
this works for me i combided @aliocharyzhkov work around
i am using formik yup and material ui
{(formik.errors.contact && formik.touched.contact) && (formik.touched.contact[i])?.number && (formik.errors.contact[i])?.number}
import React, { useState } from "react";
import { useFormik } from 'formik';
import * as yup from 'yup';
import { useDispatch } from 'react-redux';
import { useHistory } from 'react-router-dom';
// reactstrap components
import {
Card,
CardHeader,
CardBody,
CardFooter,
Container,
FormGroup,
Form,
Button,
Row,
} from "reactstrap";
import { TextField, Grid } from '@material-ui/core';
import { createContact, updateContact } from '../../actions/contact';
const phoneRegExp = /^((\\+[1-9]{1,4}[ \\-]*)|(\\([0-9]{2,3}\\)[ \\-]*)|([0-9]{2,4})[ \\-]*)*?[0-9]{3,4}?[ \\-]*[0-9]{3,4}?$/;
const validationSchemaContact = yup.object({
title: yup
.string('Enter contact title')
.max(255, 'Contact title should be of maximum 255 characters length')
.required('Contact title is required'),
contact: yup
.array()
.of(yup
.object({
person: yup
.string('Enter contact person name')
.min(3, 'Address should be of minimum 3 characters length')
.max(255, 'Contact person name should be of maximum 255 characters length'),
number: yup
.string('Enter contact person phone number')
.matches(phoneRegExp, 'Phone number is not valid')
.required('Phone number is required'),
})
.required('Required'),
)
});
const ContactForm = () => {
// dispatch and history
const dispatch = useDispatch();
const history = useHistory();
// set state contact number list
const [contactList, setContactList] = useState([{ person: "", number:"" }]);
// handle change event of the contact number
const handleContactChange = (e, index) => {
const { name, value } = e.target;
const list = [...contactList];
list[index][name] = value;
setContactList(list);
formik.values.contact = contactList;
};
// handle click event of the Remove button of contact number
const handleRemoveClick = index => {
const list = [...contactList];
list.splice(index, 1);
setContactList(list);
};
// handle click event of the Add button of contact number
const handleAddClick = () => {
setContactList([...contactList, { person: "", number:"" }]);
};
const formik = useFormik({
initialValues: {
title: '',
contact: [{
person:'',
number:''
}]
},
validationSchema: validationSchemaContact,
onSubmit: (values, { resetForm }) => {
dispatch(createContact(values, history));
resetForm();
},
onReset: (values, { resetForm }) => resetForm(),
});
return (
<>
<Header />
{/* Page content */}
<Container className="mt--7" fluid>
{/* Table School */}
<Row>
<div className="col">
<Card className="shadow">
<CardHeader className="border-0">
<h3 className="mb-0">Contact Details</h3>
</CardHeader>
<Form role="form" onSubmit={formik.handleSubmit}>
<CardBody>
<FormGroup className="mb-3">
<Grid container spacing={1} alignItems="center">
<Grid item xs={1} sm={1}>
<i className="fas fa-heading" />
</Grid>
<Grid item xs={11} sm={11}>
<TextField
fullWidth
id="title"
name="title"
label="Title"
variant="outlined"
value={formik.values.title}
onChange={formik.handleChange}
error={formik.touched.title && Boolean(formik.errors.title)}
helperText={formik.touched.title && formik.errors.title}
/>
</Grid>
</Grid>
{contactList.map((x, i) => {
return (
<Grid key={i} container spacing={1} alignItems="center">
<Grid item xs={1} sm={1}>
<i className="ni ni-mobile-button">{(1 + i)}</i>
</Grid>
<Grid item xs={10} sm={10}>
<TextField style={{width:'50%'}}
id="person"
name="person"
label={'Contact Person'}
variant="outlined"
value={x.person}
onChange={e => handleContactChange(e, i)}
error={(formik.errors.contact && formik.touched.contact) && (formik.touched.contact[i])?.person && Boolean((formik.errors.contact[i])?.person)}
helperText={(formik.errors.contact && formik.touched.contact) && (formik.touched.contact[i])?.person && (formik.errors.contact[i])?.person}
/>
<TextField style={{width:'50%'}}
id="number"
name="number"
label={'Contact Number'}
variant="outlined"
value={x.number}
onChange={e => handleContactChange(e, i)}
error={(formik.errors.contact && formik.touched.contact) && (formik.touched.contact[i])?.number && Boolean((formik.errors.contact[i])?.number)}
helperText={(formik.errors.contact && formik.touched.contact) && (formik.touched.contact[i])?.number && (formik.errors.contact[i])?.number}
/>
</Grid>
<Grid item xs={1} sm={1} >
{contactList.length !== 1 && <RemoveCircleOutlineIcon className="mr10" onClick={() => handleRemoveClick(i)}/>}
{(contactList.length - 1 === i && !(contactList.length > 4)) && <AddCircleOutlineIcon className="ma10" onClick={handleAddClick}/>}
</Grid>
</Grid>
);
})}
</FormGroup>
</CardBody>
<CardFooter className="py-2 text-right">
<Button className="my-2" variant="contained" color="primary" type="submit">
Submit
</Button>
</CardFooter>
</Form>
</Card>
</div>
</Row>
</Container>
</>
);
};
export default ContactForm;
@hassaans I use a ternary operator to check if there's an error, and if there is I cast the object to the correct type. For example,
errors.memebers[i] ? (errors.members[i] as IMember).name : ''
. Fortunately, there are just a few places where I have to use this hack. Moreover, I added a comment to remind me to refactor those line when the issue is fixed in the library.
So glad I found this issue, I thought I was going crazy with this error. Thank you for reporting this and posting the workaround.
For possible undefined
, optional chaining will work:
errors.members?.[i] ? (errors.members[i] as IMember).name : ''
Faced it today. And yes, I thought I was going crazy with this error too. Maybe there should be added a warning to docs or something?
error={touched.test && Boolean((errors.test?.[0] as ErrorsAmount)?.amount)}
helperText={(errors.test?.[0] as ErrorsAmount)?.amount}
That worked for me.
There exists an undocumented function GetIn
in fomik that one can use to retrieve the form error in case it's located in a nested object. This is how I solved it.
@filip-dahlberg it's documented https://formik.org/docs/api/fieldarray#fieldarray-validation-gotchas
@filip-dahlberg you use the getIn function as indicated in the documentation by @peter-visma ?
update: I tested it in a project using getIn and it solved my problem.
Both getIn(errors.col?.[index], 'key')
and (errors.test?.[index]) as SomeType)?.[key]
are work very well. ;)
I am experiencing this error with a Object
Property 'text' does not exist on type 'FormikErrors<{ email: string; password: string; }>
Are there any chances for a better solution to this problem?
Are there any chances for a better solution to this problem?
There is a better solution using 'getIn,' which is not explicitly mentioned in the documentation, except when used in conjunction with something else, as shown here: https://formik.org/docs/api/connect. An example usage is as follows: getIn(errors, 'user.[${index}].email')
error={(formik.errors.contact && formik.touched.contact) && (formik.touched.contact[i])?.number && Boolean((formik.errors.contact[i])?.number)}
This one worked well for me
error={(formik.errors.contact && formik.touched.contact) && (formik.touched.contact[i])?.number && Boolean((formik.errors.contact[i])?.number)}
🐛 Bug report
Current Behavior
Suppose we have the following types:
Then the type of
errors.members[i]
is inferred as'string | FormikErrors<IMember>'
which produces this errorProperty 'name' does not exist on type 'string | FormikErrors<IMember>'.
when one tries to accesserrors.members[i].name
.Expected behavior
The type of
errors.members[i]
should be'FormikErrors<IMember>'
.Reproducible example
Notice the error on line 57: https://codesandbox.io/s/react-typescript-uucgn
Suggested solution(s)
The solution is to delete
| string | string[]
in this line.Additional context
Your environment