Exposing error values to component in Redux Form - reactjs

I have a component that uses redux form and validates like so:
const validate = values => {
// Initialize errors constant.
const errors = {};
const testEmail = value =>
value && !/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value) ?
errors.email = 'Invalid email address' : undefined;
// Only run test if email exists.
testEmail(values.email);
// Show error if value is touched and not filled out.
const required = ['email', 'team', 'gameID', 'season', 'winner', 'bet', 'contractName'];
required.map( input => {
if (!values[input]) {
return errors[input] = 'Required'
};
return null;
});
return errors;
}
const SportsFormWithCSS = CSSModules(SportsForm, styles, { allowMultiple: true });
SportsForm = reduxForm({
form: 'SportsForm',
validate
})(SportsFormWithCSS);
Is there a way I can add the error values to props, for instance? I have tried a couple implementations with little luck.

Related

Validation does not work in react-hook-form with dynamic name fields

I use react-hook-form and yup-validation. An array with custom names comes to me from the backend. Validation doesn't work for me. What am I doing
export const createValidationSchema = (arr) => {
const validationObject = {};
arr?.forEach(({name}) => {
validationObject[name] = string().required("Required field")
});
return object().shape({
editFields: array()
.of(object().shape(validationObject))
.required("Required field")
});
};
const { control, register, handleSubmit, formState: { errors, isValid } } = useForm<Form>({
resolver: yupResolver(createValidationSchema(documentDetail?.template?.items)),
mode: "onBlur"
});
const { fields, append } = useFieldArray<Form>({
control,
name: "editFields",
});
below in JSX:
{fields?.map(({ name, title, type, id }) => (
{type === "STRING" && (
<InputField
validationType="underlineText"
{...register(name)}
required
/>
)}
)
but errors always empty. What am I doing wrong?
My mistake. In validationObject I have an object, and in the schema I assigned it as an array

Antd Custom validator and validateFields are not working together as expected

I'm working on a form (Ant Design <Form>) where I want a custom validation of phone number (as it depends on country field and has some extra logic), and other fields validated by antd built-in features, so when user submits the form I want all fields to be validated with validateFields() passed as an array of field names showing validation errors as usual (red message underneath and an error message in console), but all I'm getting is just a warning message in the console.
Here's a minimal reproduction
Am I missing something about how validator function works?
make a if else check in your validatePhone function if value is not empty then run your code and if value is empty simply send a callback in else condition. like that
const validatePhone = (rule: any, value: any, callback: any) => {
if (value) {
const countryName = form.getFieldValue('country');
const country = CountryList.getCode(countryName);
const phoneNumber = parsePhoneNumberFromString(value, country as CountryCode);
console.log(form.getFieldValue('country'));
if (countryName && phoneNumber && phoneNumber.isValid()) {
updatePhonePrefix(prefix.concat(phoneNumber.countryCallingCode as string));
callback();
} else {
callback(`Phone number is not valid for ${countryName}`);
}
} else {
callback();
}
};
what the antd docs recommend is to use async or try-catch block
option 1:
const validatePhone = async (rule, value) => {
// check failing condition
throw new Error('Something wrong!');
}
option 2:
const validatePhone = (rule, value, callback) => {
try {
throw new Error('Something wrong!');
} catch (err) {
callback(err);
}
}
Here how I handled the error in antd form
import React, { useState } from 'react';
import { Form, Input } from 'antd';
function MyCustomForm(props: any) {
const [form] = Form.useForm();
const [validateFieldsName, setValidateFieldsName] = useState<string[]>([]);
const handleValidateFieldNames = (name: string) => {
const isFieldName = validateFieldsName.find(
(fieldName) => fieldName === name
);
if (isFieldName) return 'onChange';
return 'onBlur';
};
return (
<Form form={form} layout="vertical">
<Form.Item
name="contactNumber"
label="Contact Number"
validateTrigger={handleValidateFieldNames('contactNumber')}
rules={[
{
required: true,
message: 'Please enter contact number!',
},
{
validator(_, value) {
if (!value || value.length === 10) {
return Promise.resolve();
}
return Promise.reject('Please enter 10 digit Number!');
},
},
]}
>
<Input
type="number"
placeholder="Contact number"
onBlur={() =>
setValidateFieldsName([...validateFieldsName, 'contactNumber'])
}
/>
</Form.Item>
</Form>
);
}
export default MyCustomForm;

How can I validate an input's value on user keyboard entry?

How to validate form's input value on user typing or on change? I am trying to read state but it is kind of late/ not realtime.
I am thinking of using a class variable/ property and mutate it, but I am afraid that it will offend the React's principal.
Is there a proper way to create realtime form validation like this in React?
Validation is so widely used that we can find dozens of good ways to do that with react. I like to use the following:
Instead of just hold the value of your inputs on state, you could make a more complex object for each one. Let's begin defining a form with 2 inputs: name and age. The first step would be describe the form in state. Something like that:
state = {
form:{
name:{
value : '',
valid : true,
rules:{
minLength : 3
}
},
age:{
value : '',
valid : true,
rules:{
isNumber : true
}
}
}
}
There we have it! We now have 2 inputs that are valid on the initial render and have their own validation rules(isNumber, minLength). Now we need to write a function that validates the state on the fly. Let's write it then:
onChangeHandler = (key, value) =>{
this.setState(state =>({
...state,
form:{
...state.form,
[key]:{
...state.form[key],
value,
valid : validate(value, state.form[key].rules)
}
}
}))
}
Now we have a form described in state and a handler that updates the state onChange and validate the value of the input on each call. Now the only thing to do is write your validate() function and you are ready to go.
validate = (value, rules) => {
let valid = true
for (let key in rules) {
switch (key) {
case 'minLength':
valid = valid && minLengthValidator(value, rules[key])
break
case 'isNumber':
valid = valid && isNumberValidator(value)
break
default: break
}
}
return valid
}
Now the validators...
minLengthValidator = (value, rule) => (value.length >= rule)
isNumberValidator = value => !isNaN(parseFloat(value)) && isFinite(value)
Done! Now call your inputs like that:
render(){
const { form } = this.state
return(
<TextField value={form.name.value} onChange={e => this.onChangeHandler('name',e.target.value)} />
)
}
Every time that the input changes the validate function will be triggered, now you have real time form validation, is up to you apply the respectives styles according to the valid prop.
we have to define a validateProperty(input) and use it in our onChange method. here is a basic example how to implement it.first define your state. assuming that i have a form with username and password inputs.
state = { account: { username: "", password: "" }, errors: {} };
validateProperty = (input) => {
if (input.name === "username") {
if (input.value.trim() === "") return "username is required";
}
if ((input.name = "password")) {
if (input.value.trim() === "") return "password is required";
}
};
now we have to use it in on handleChange
e.currentTarget returns input field and i named it as input and did object destructuring
handleChange = ({ currentTarget: input }) => {
const errors = { ...this.state.errors };
const errorMessage = this.validateProperty(input);
if (errorMessage) errors[input.name] = errorMessage;
else delete errors[input.name];
const account = { ...this.state.account };
account[input.name] = input.value; //currentTarget returns input field
this.setState(() => ({ account, errors }));
};
for each input field I added "name" attribute. so errors[input.name] will be errors[e.currentTarget.name] if name="username" username field, if name="password" password field.

"Cannot read property 'nationalCode' of undefined" error in redux-form

I use redux-form for my reactjs applicaion, in validate.js file when i want validate my formSection "personFields" code will occur with "Cannot read property 'nationalCode' of undefined" error
// validate.js file
export default values => {
const errors = {};
errors.personFields = validatePersonFields(values.personFields);
return errors
};
const validatePersonFields = values => {
const errors = {};
if (!values.nationalCode) {
errors.nationalCode = 'it is required';
}
return errors;
};
how can I solve this problem?
values.personFields must be undefined:
const validatePersonFields = (values = {}) => {
const errors = {};
if (!values.nationalCode) {
errors.nationalCode = 'it is required;
}
return errors;
};
This will at least allow you to continue when no field values are present.

React Redux and side effects explanation

I'm wrapping my forms to provide automatic validation (I don't want to use redux-form).
I want to pass an onSubmit handler which must be fired after every input in form is validated: but how do I wait for form.valid property to turn into true && after wrapping submit was fired? I'm missing some logic here!
//in my Form.js hoc wrapping the forms
#autobind
submit(event) {
event.preventDefault();
this.props.dispatch(syncValidateForm({ formName: this.props.formName, form: this.props.form }));
// ==> what should I do here? Here I know submit button was pressed but state is not updated yet with last dispatch result reduced!
//if(this.props.form.valid)
// this.props.submit();
}
render() {
return (
<form name={this.props.formName} onSubmit={this.submit}>
{ this.props.children }
</form>
);
//action.js validating given input
export const syncValidateInput = ({ formName, input, name, value }) => {
let errors = {<computed object with errors>};
return { type: INPUT_ERRORS, formName, input, name, value: errors };
};
//action.js validating every input in the form
export const syncValidateForm = ({ formName, form }) => {
return dispatch => {
for(let name in form.inputs) {
let input = form.inputs[name];
dispatch(syncValidateInput({ formName, input, name: input.name, value: input.value }));
}
};
};
//in my reducer I have
case INPUT_ERRORS:
let errors = value;
let valid = true;
let errorText = '';
_.each(errors, (value, key) => {
if(value) {
valid = false;
errorText = `${errorText}${key}\n`;
}
});
form.inputs[name].errors = errors;
form.inputs[name].valid = valid;
form.inputs[name].errorText = errorText;
_.each(form.inputs, (input) => form.valid = !!(form.valid && input.valid));
return state;
Help!
Depending on your build config you could use Async/Await for your submit function. Something like
async submit(event) {
event.preventDefault();
const actionResponse = await this.props.dispatch(syncValidateForm({ formName: this.props.formName, form: this.props.form }));
if (actionResponse && this.props.form.valid) { //for example
// finish submission
}
}
And I think you will need to update your syncValidateForm slightly but this should put you on the right path.

Resources