Sync validation to throw form level error (rather then field level) - redux-form

In sync validation per - https://redux-form.com/7.1.2/examples/syncvalidation/ - I was trying to throw a form level error. Just like when we do throw new SubmissionError({ _error:'form level error from on submit' }), however I am not able to figure it out. I tried using the special _error key and even just error key like so:
reduxForm({
validate: values => ({ error:'form level??', _error:'form level alt??' })
})
However it is not setting FormProps error value. Does anyone know if this is supported or how to achieve this?

To pass form-level error to form after validation you should add _error property to object returned by redux-form config validate function.
The error will be passed as error prop to your form component.
For example:
import React from 'react';
import { Field, reduxForm } from 'redux-form';
const ExampleForm = (props) => {
const { handleSubmit, pristine, submitting, error } = props;
return (
<form onSubmit={handleSubmit}>
<div>
<label>Name</label>
<div>
<Field
name="name"
component="input"
type="text"
placeholder="Name"
/>
</div>
</div>
<div>Error: {error}</div>
<div>
<button type="submit" disabled={pristine || submitting}>Submit</button>
</div>
</form>
);
};
const validate = (values) => ({
_error: `form level error for firstName = '${values.name || ''}'!`,
});
export default reduxForm({
form: 'exampleForm',
validate,
})(ExampleForm);

Related

React formik form onsubmit event callings many times?

import { Formik, Form, Field } from "formik";
import { Button } from "antd";
const AddUser = () => {
const initialValues = {
name: "",
};
return (
<>
<Formik
initialValues={initialValues}
onSubmit=(values) => {
alert("hi");//calling mamy times
Here added api call (post method)
}}
>
{({ isValid, submitForm, isSubmitting, values }) => {
return (
<Form>
<Field
name="name"
label="Name"
placeholder="Dataset Name"
/>
<Button
type="primar"
htmltype="submit"
loading=(props.addingdata) // this is my reducer state intial was false after post call request became true and success state value false
>
Add Dataset
</Button>
</Form>
);
}}
</Formik>
</div>
</>
);
};
export default AddUser;
I have simple formik form antd button I have used when click submit button post api calling twice and thrice even If I added loading property in button why its happening like this?

yup validation with dynamic data

I have a yup validation with API response requirement for a react hook form.
My form is like:
import React from "react";
import useForm from "react-hook-form";
import { schema } from "./schema";
import { useTestProps } from "./useTestProps";
function Sub({ title, showForm }) {
const { register, handleSubmit, errors } = useForm({
validationSchema: schema
});
const { data } = useTestProps();
const onSubmit = (data) => {
//submit form
};
// console.log(errors);
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<label>{title}</label>
<input type="text" name="iip" ref={register} />
<br />
{errors.iip && <p>{errors.iip.message}</p>}
<br />
<label>Email</label>
<input type="text" name="email" ref={register} />
<br />
{errors.email && <p>{errors.email.message}</p>}
<br />
<input type="submit" />
</form>
</div>
);
}
export default Sub;
Validation schema of the form is like this,
//schema.js
import * as yup from "yup";
export const schema = yup.object().shape({
iip: yup
.string()
.required()
.test("validate with testProps", "iip not found", (value) => {
// validate that value should be one of testProps
// if value is in data, return true, or return false
return true
}),
email: yup.string().email().required()
});
My requirement is to validate iip field with data returned from API call in useTestProps which is like this,
{
"data": ["test1", "test2", "test3"]
}
How can I access data in schema object test where I can compare user entered value with JSON response?

Form-level validation does not behave as expected

Using redux-form 7.0.3, I'm unable to get the Field validate API to do anything.
First, I created a basic, minimal example templated off of the docs.
import React from 'react'
import { Field, reduxForm } from 'redux-form'
// removed validation fns
const required = value => {
console.log('Test of validation fn')
return (value ? undefined : 'Required')
}
// unchanged
const renderField = ({
input,
label,
type,
meta: { touched, error, warning }
}) =>
<div>
<label>
{label}
</label>
<div>
<input {...input} placeholder={label} type={type} />
{touched &&
((error &&
<span>
{error}
</span>) ||
(warning &&
<span>
{warning}
</span>))}
</div>
</div>
// removed fields & made into React Component
class FieldLevelValidations extends Component {
render(){
const { handleSubmit } = this.props
return (
<form onSubmit={handleSubmit}>
<Field
name="test"
type="text"
component={renderField}
label="Test Component"
validate={required}
/>
<div>
<button type="submit">
Submit
</button>
</div>
</form>
)
}
}
export default reduxForm({
form: 'fieldLevelValidation'
})(FieldLevelValidations)
From this I would assume that the forms reducer processes an action that sets a prop that can be accessed in renderField, but it does not. Both error and warning are undefined, and required is never called. Additionally, there is no isValid or the like property set in the store. I don't know if this is a bug or if I'm missing some critical piece, but I would expect more to be happening here. Also, some greater detail in the docs would be nice.

Redux form textarea error handling

I notice in a textarea field, sync-error handling doesn't behave like an input field. For example, after displaying a sync-error on a form's input field the error happily disappears when I start typing in the field. On a textarea field, the sync-error just sits there when I start typing (or when I leave the field).
Basically, onBlur is not setting touched to true when I exit the field.
What extra things should I consider when dealing with sync-error handling on a textarea field in a Redux form?
redux-form ^6.x.x changed a lot of its implementation compared to v4 and v5
If you post you sample code, I can help you better though.
I think what u meant, normal field works fine but field
That means your reduxForm(...) works fine, there might be the problem in your error handler function or field rendering function.
I leave simple code example
// validate
const validate = (values) => {
const errors = {};
if(!values.title) {
errors.title = 'title require';
}
if (!values.categories) {
errors.categories = 'categories require';
}
if (!values.content) {
errors.content = 'content require';
}
return errors;
};
// renderField
const renderField = ({ input, label, type, textarea, meta: { touched, error, warning, invalid } }) => {
const textareaType = <textarea {...input} placeholder={label} type={type} className={`form-control ${touched && invalid ? 'has-danger' : ''}`}/>;
const inputType = <input {...input} placeholder={label} type={type} className={`form-control ${touched && invalid ? 'has-danger' : ''}`}/>;
return (
<div>
<label>{label}</label>
<div>
{textarea ? textareaType : inputType}
{touched && ((error && <span>{error}</span>) || (warning && <span>{warning}</span>))}
</div>
</div>
);
};
// React render
<form onSubmit={handleSubmit}>
<Field name="title" component={renderField} type="text" label="Title" />
<Field name="categories" component={renderField} type="text" label="Categories" />
<Field name="content" component={renderField} type="text" label="Content" textarea={true} />
<button type="submit" disabled={pristine || submitting} className="btn btn-primary">Submit</button>
</form>
reduxForm({
form: 'TestComponent', // Name of the form
validate // <--- validation function given to redux-form
})(TestComponent)

Using redux-form I'm losing focus after typing the first character

I'm using redux-form and on blur validation. After I type the first character into an input element, it loses focus and I have to click in it again to continue typing. It only does this with the first character. Subsequent characters types remains focuses. Here's my basic sign in form example:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import * as actions from '../actions/authActions';
require('../../styles/signin.scss');
class SignIn extends Component {
handleFormSubmit({ email, password }) {
this.props.signinUser({ email, password }, this.props.location);
}
renderAlert() {
if (this.props.errorMessage) {
return (
<div className="alert alert-danger">
{this.props.errorMessage}
</div>
);
} else if (this.props.location.query.error) {
return (
<div className="alert alert-danger">
Authorization required!
</div>
);
}
}
render() {
const { message, handleSubmit, prestine, reset, submitting } = this.props;
const renderField = ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<input {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
return (
<div className="row">
<div className="col-md-4 col-md-offset-4">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))} className="form-signin">
<h2 className="form-signin-heading">
Please sign in
</h2>
{this.renderAlert()}
<Field name="email" type="text" component={renderField} label="Email Address" />
<Field name="password" type="password" component={renderField} label="Password" />
<button action="submit" className="btn btn-lg btn-primary btn-block">Sign In</button>
</form>
</div>
</div>
);
}
}
function validate(values) {
const errors = {};
if (!values.email) {
errors.email = 'Enter a username';
}
if (!values.password) {
errors.password = 'Enter a password'
}
return errors;
}
function mapStateToProps(state) {
return { errorMessage: state.auth.error }
}
SignIn = reduxForm({
form: 'signin',
validate: validate
})(SignIn);
export default connect(mapStateToProps, actions)(SignIn);
This happens because you're re-defining renderField as a new component every time you render which means it looks like a new component to React so it'll unmount the original one and re-mounts the new one.
You'll need to hoist it up:
const renderField = ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<input {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
class SignIn extends Component {
...
render() {
const { message, handleSubmit, prestine, reset, submitting } = this.props;
return (
<div className="row">
<div className="col-md-4 col-md-offset-4">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))} className="form-signin">
<h2 className="form-signin-heading">
Please sign in
</h2>
{this.renderAlert()}
<Field name="email" type="text" component={renderField} label="Email Address" />
<Field name="password" type="password" component={renderField} label="Password" />
<button action="submit" className="btn btn-lg btn-primary btn-block">Sign In</button>
</form>
</div>
</div>
);
}
}
...
As #riscarrott mentioned, put renderField outside of component class .
But I am still losing focus .. And after testing, I concluded the re-rendering is done because of using curried function (return another function, and not return element . directly) .
const const renderField = (InputComponent = 'input') => ({ input, label, type, meta: { touched, invalid, error } }) => (
<div class={`form-group ${touched && invalid ? 'has-error' : ''}`}>
<label for={label} className="sr-only">{label}</label>
<InputComponent {...input} placeholder={label} type={type} className="form-control" />
<div class="text-danger">
{touched ? error: ''}
</div>
</div>
);
Then, if your renderField is a curried function :
then , don't do 😔😔😔😔:
//.....
<Field name="email" type="text" component={renderField('input')} label="Email Address" />
<Field name="desc" component={renderField('textarea')} label="Email Address" />
But , do the following 🙂🙂🙂🙂 :
// outside component class
const InputField = renderField('input');
const TextAreaField = renderField('textarea');
// inside component class
<Field name="email" type="text" component={InputField} label="Email Address" />
<Field name="desc" component={TextAreaField} label="Email Address" />
What worked for me was refactoring arrowFunction-based Component to class-based Component as the behavior of InputForm components was weird. Every time the value of each input was changed they all rerendered even after splitting each inputType to separated components. There was nothing else left to fix but changing main component to class-based. I guess it may be caused by redux-form itself.
This can also happen if you have defined styled-components inside your render function.
You should define them outside your class.
Like this:
const Row = styled.div`
justify-content:center;
`;
const Card = styled.div`
width:18rem;
padding:1rem;
`;
class Login extends Component{
i have the same problem. i resolved mine by changing the component to Class component and i removed all the css style config from render().
I had the same problem. I solved it when I added my react redux form to the store in the createForms():
export const ConfigureStore = () => {
const store = createStore(
combineReducers({
tasks: Tasks,
task: Task,
image: Image,
admin: Admin,
pageId: PageID,
fieldValues: FieldValues,
formValues: FormValues,
...createForms({
createTask: initialTask,
editTask: initialEditTask
})
}),
applyMiddleware(thunk, logger)
);
return store;
}
I had the same problem, and none of the answers worked for me.
But thanks to Advem's answer I got an idea of what could be wrong:
My form required accordion UI, and for that I had state variable in it:
const ConveyorNotificationSettingsForm = (props) => {
const {handleSubmit, formValues, dirty, reset, submitting} = props;
const [expandedSection, setExpandedSection] = useState(null);
...
with only one expanded section, that with its index equal to expandedSection .
After I extracted the accordion to a separate functional component and moved useState there, the problem was gone.
actually, this is a problem with the function component. I used a class-based component with redux form and my problem solved. I don't know the exact reason but redux form re-renders when we enter the first word and losses focus. use class-based components whenever you want to use redux form.
class StreamCreate extends React.Component{
rendorInput(formProps){
return <input {...formProps.input} />;
}
render(){
return (
<Container maxWidth="lg">
<form action="">
<Field name="title" component={this.rendorInput}/>
<Field name="description" component={this.rendorInput} />
</form>
</Container>
)
}
}
export default reduxForm({
form: 'createStream'
})( StreamCreate);

Resources