Form-level validation does not behave as expected - redux-form

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.

Related

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

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

Unit test case for React Component using enzyme

I am working on reactjs 5.X version. I am writing UT with enzyme. Below is my reusable component with prop arguments. Not able to find the div tags inside of the const component.Can you help me how to get the div tags using classname or using find function to get div tags.
import React from 'react';
import styles from './democheck.css';
const democheck = ({ input, cb,
required, testCheckbox, checked, label, meta: { error } }) => (
<div id="hierrdivid" className={styles.testDivColumn}>
<div className={styles.testDiv}>
<br />
<span className={styles.testLabel}>
{label}
</span>
{required && <span className={styles.error}>
*
</span>}
<input
{...input}
name={`form-field-${input.name}`}
checked={checked}
id={input.name}
className={testCheckbox ? styles.testCheckboxError :
styles.testCheckbox}
type="checkbox"
onChange={() => {
if (cb) {
cb(document.getElementById(input.name).checked);
}
}}
/>
</div>
<div className={styles.testerrorDiv}>
{testCheckbox &&
<div className={styles.testerrorLabel}>
{label} {error}
</div>}
</div>
</div>
);
democheck.propTypes = {
input: React.PropTypes.objectOf(React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.func
])),
cb: React.PropTypes.func,
label: React.PropTypes.string,
meta: React.PropTypes.shape({}),`enter code here`
required: React.PropTypes.bool,
checked: React.PropTypes.bool,`enter code here`
testCheckbox: React.PropTypes.bool
};

redux-form and react-select with options appearing as user types

I have a redux-form with a react-select. The expected behaviour is that, as I type in the select field, I call the redux action (by using OnInputChange). But I don't know how to call the action. The line that calls the action is commented in the snippet below, because it fails (this.props.getArtistSearch(value)). Any ideas of how to properly call the action as user types in?
class FormApplication extends React.Component {
submit(values) {
this.props.submitForm(values)
}
getArtist(value){
//this.props.getArtistSearch(value) --> props is undefined
console.log(value)
}
render() {
const { handleSubmit } = this.props
return (
<form className='content text padding-top-0' onSubmit={handleSubmit(this.submit.bind(this))}>
<div className='row adjust-form-row'>
<div className='col-md-6 last-lineup'>
<div className='row adjust-form-row'>
<div className='col-md-6'>
<div className='form-group'>
<Field
name='dl_artistname'
options={this.props.gap.artistSearch}
component={props => (
<Select
{...props}
name={props.name}
onInputChange={this.getArtist}
onChange={(value) => {
this.props.requestArtistInstance({id: value.dl_artistid })
return props.input.onChange(value != null ? value.dl_artistid : null)}
}
onBlur={() => props.input.onBlur(props.input.value)}
options={props.options}
//loadOptions={getOptions}
clearable={false}
cache={false}
backspaceRemoves={false}
valueKey='dl_artistid'
labelKey='dl_name'
value={props.input.value || ''}
isLoading={false}
disabled={false}
/>
)}
/>
</div>
</div>
</div>
</div>
</div>
</form>
)
}
}
const mapDispatchToProps = dispatch => ({
getArtistSearch: (text) => {
dispatch(getArtistSearch(text))
},
submitForm: (values) => {
dispatch(submitForm(values))
}
})
Going through your code, I noticed that the custom method you defined, getArtist was not bound to your React context so props will be undefined. Two possible approaches for this are:
1) bind it in the constructor method
constructor(){
super();
this.getArtist = this.getArtist.bind(this);
}
2) Alternatively, bind it in the Select component(Not ideal tho')
onInputChange={this.getArtist.bind(this)}

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