redux form, Field component issues - redux-form

I have some issues using "Field" component in Redux Form in my code. Just follow the simple example on redux-form website.
/* MyForm.jsx */
...
import { Field, reduxForm } from 'redux-form';
class MyForm extends Component {
...
<form onSubmit={handleSubmit(...)}>
<div>
<label>First Name</label>
<div>
<Field name="firstName" component="input" type="text" placeholder="First Name"/>
</div>
</div>
...
}
export default reduxForm({
form: 'myForm'
})(MyForm);
Here is the parent component that use the redux form.
/* page.jsx */
import MyForm from './MyForm';
...
<MyForm/>
...
The form does not render and in console the error says:
React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components).
Uncaught Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
However, if I use material-ui or bootstrap form field, it works fine.
I mounted the form reducer before render the form:
import {reducer as formReducer} from 'redux-form';
const myReducer = combineReducers({
...
form: formReducer
});
Any ideas why Fields component does not work?
Thanks,

Try upgrading your redux to 6.0. Field component started at v6.0, I believe.

Related

Redux-form validation breaks when using multiple components with the same form name

I run into the validation issue using multiple components decorated with same form name.
Let's say we have SimpleForm1 and SimpleForm2. When rendering Only SimpleForm1 with the name field validation works as expected, as well as when rendering SimpleForm2 with the surname field. But when rendering them both on a single page validation for SimpleForm1 is broken.
The question is how to avoid such behaviour and make both validation functions work.
Here is a fiddle which illustrates my problem
It's not a good idea to use same names for multiple forms.
As i understand you need to dynamically add form inputs(SimpleForm2 in your example) and have possibility to submit both forms with one button.
If yes, so you can add just an input to first form, you don't need second form.
Form:
const SimpleFormComponent1 = props => {
const {handleSubmit, pristine, reset, submitting, renderBoth} = props;
const onSubmit = (values) => {
alert(JSON.stringify(values));
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
...
{
renderBoth &&
<Field
name="surname"
type="text"
component={renderField}
label="Surname"
validate={validateSurname}
/>
}
...
</form>
)
};
Render function:
render() {
const {renderBoth} = this.state;
return (
<div>
<div className='forms'>
<SimpleForm renderBoth={renderBoth}/>
</div>
<button
type='button'
onClick={this.handleClick}>
{renderBoth ? 'Show Single' : 'Show Both'}
</button>
</div>
);
}
Updated example
SOLUTION 1
I was having interference with invalid and valid props of the Redux Form because I wrote the validate function on ReduxForm() in every file of my main form component.
You haven't to change the form name to solve this. You have to put the validation function on the parent component.
Here is an example:
[CONTEXT]
I have 3 Components:
1. Main component to put the form ('editUserForm') elements (, , ...)
2. Field1 of the form ('editUserForm') that changes user's complete name.
3. Field2 of the form ('editUserForm') that changes user's email.
[SOLUTION]
MAIN COMPONENT:
Inside the main frame, you call reduxForm() (it creates a decorator with which you use redux-form to connect your form component to Redux. More info here Redux Form docs). Your code would be like this:
import...
class MainFrame ... {
...
<form ...>
<Field1 />
<Field2 />
</form>
...
}
const validate ({ name, email }, props) => {
errors={}
// Validation around Field1
if (name === ...) errors.name = "Name error passed to Field1 component";
// Validation around Field2
if (email === ...) errors.email= "Email error passed to Field2 component";
return errors;
}
...
export default reduxForm({
form: 'editUserForm',
validate // <--------- IMPORTANT: Only put this function on the parent component.
})(MainComponent);
FIELD1 & FIELD2 COMPONENTS:
This code is for the 2 children components. IMPORTANT: You call reduxForm() without validate Redux Form docs synchronous function.
import...
class MainFrame ... {
...
const { invalid } = this.props;
...
<inputs
error={invalid}
>
...
</input>
...
}
// IMPORTANT: Don't put the validation function in Field1 and Field2 components, because their local validations will interfere with the validation of the other Field component.
reduxForm({
form: 'editUserForm'
})
Now, the props: valid and invalid will work perfectly inside the children components (Field1 and Field2).
SOLUTION 2
User Redux Form FormSection (docs) to split forms into smaller components that are reusable across multiple forms.

Unclear syntax in redux-form

Having trouble understanding the syntax in redux-form...
In redux-docs
const renderField = (field) => (
<div className="input-row">
<input {...field.input} type="text"/>
{field.meta.touched && field.meta.error &&
<span className="error">{field.meta.error}</span>}
</div> )
How is the spread operator used inside the input and why does the following returnes an error:
<input {...field.input, id} type="text"/>
The second syntax I dont understand is:
class MyCustomInput extends Component {
render() {
const { input: { value, onChange } } = this.props <---??
return (
<div>
<span>The current value is {value}.</span>
<button type="button" onClick={() => onChange(value + 1)}>Inc</button>
<button type="button" onClick={() => onChange(value - 1)}>Dec</button>
</div>
)
}
}
What is:
input: { value, onChange }
?
Queston 1:
The error from using {...field.input, id} should be something like the following:
Syntax error: ...: Unexpected token, expected }
If you already have props as an object, and you want to pass it in
JSX, you can use ... as a “spread” operator to pass the whole props
object (Spread Attributes)
So it appears the JSX attribute spread is not exactly the same as a es2015 spread. I believe it is limited to just the examples in the react docs.
Question 2:
The Field component passes these props to your wrapped component MyCustomInput
input.onChange(eventOrValue) : Function
A function to call when the form field is changed. It expects to either receive the React SyntheticEvent or the new value of the field.
input.value: any
The value of this form field. It will be a boolean for checkboxes, and a string for all other input types. If there is no value in the Redux state for this field, it will default to ''. This is to ensure that the input is controlled. If you require that the value be of another type (e.g. Date or Number), you must provide initialValues to your form with the desired type of this field.
In other words when MyCustomInput fires onChange the Field component will dispatch an action to update the form store.
The value prop is used to maintain the state MyCustomInput.

How to set initialValue with semantic-ui-redux-form-fields

I'm trying to create an Edit form w/ semantic-ui-redux-form-fields. The forms work fine w/ a blank starting point, but for an edit form where I have initial values, I haven't been able to get it to work.
If I'm using straight redux-form, this works fine for initializing the form as long as I have a prop of initialValues passed into my form component.
Using that logic, I would think to all I would need to do is change the Field#component attribute from "input" to {Input} (/w the appropriate import). Note that I never see currentValue being passed in via props. It's not clear how this prop would ever get populated.
Props:
{initialValues: {"first_name": "Bob"}}
Working Component:
import { Form, Button } from 'semantic-ui-react'
import { reduxForm, Field} from 'redux-form'
...
return <Field component="input" type="text" name='first_name'/>
Not Working Component:
import { Form, Button } from 'semantic-ui-react'
import { reduxForm, Field} from 'redux-form'
import { Input } from 'semantic-ui-redux-form-fields'
...
return <Field component={Input} type="text" name='first_name'
currentValue={this.props.currentValue}/>
The Input component in semantic-ui-react has a defaultValue prop that I believe you should be able to use to initialize the Field with data since you are passing it the Input in it's component prop.
<Field
component={Input}
defaultValue={this.props.currentValue}
name='first_name'
type='text'
/>

asyncValidation on key strokes and throttled (not on blur)

The redux-form asyncValidation is great but it only works on blur. Is it possible to make it happen during key presses, and throttled? So it runs only every 300ms and on final value?
Is it possible? The short answer is yes.
Is it tricky? Well, as you mentioned, theres the fact that the asyncValidation for redux-form option only works for the onBlur and you'd instead want it to work onChange.
So you could fork and add this feature into redux-form, so you can do this:
#reduxForm({
form: 'login',
asyncValidate: validator,
//asyncBlurFields: ['username','password'],
asyncChangeFields: ['username','password'], /* add this new option */
})
For the deboucing part, you'd want to somehow debounce the onChange handler rather than the validate function itself, which brings up another option...
redux-form exports its internal action creators which may be enough to hack it together. Particularly, there's the stopAsyncValidation action creator that lets you pass field-level async errors directly into a form. Pair that with the onChange prop for Field, and you actually have the right pieces to get it done like this:
import React from 'react'
import { Field, reduxForm, stopAsyncValidation } from 'redux-form'
import _debounce from 'lodash.debounce'
import renderField from './renderField'
#reduxForm({form: 'debouncedOnChangeValidation'})
class DebouncedOnChangeValidationForm extends React.Component {
customValidate = () => {
const { form, dispatch } = this.props
dispatch(stopAsyncValidation(form, { username: "thats wrong..." })) /* pass in async error directly! */
}
debounceValidate = _debounce(this.customValidate, 1000) /* decorate with debounce! */
render() {
const { handleSubmit, pristine, reset, submitting} = this.props
return (
<form onSubmit={handleSubmit}>
<Field name="username" type="text"
component={renderField} label="Username"
onChange={this.debounceValidate} /* bind validation to onChange! */
/>
<div>
<button type="submit" disabled={submitting}>Submit</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
</div>
</form>
)
}
}
Additionally, to access the form values for performing validation, you'd need to use the getFormValues selector.
Of course this won't be as robust as a more built-in solution, but it may work well enough for some use cases.

How to add form validation pattern in Angular 2?

I have a simple form that needs to validate if the beginning and the end of the input is not space.
In HTML5, I will do this:
<input type="text" pattern="^(?!\s|.*\s$).*$">
What is the right property for validation pattern in the new Angular 2 ngControl directive? The official Beta API is still lacking documentation on this issue.
Now, you don't need to use FormBuilder and all this complicated valiation angular stuff. I put more details from this (Angular 2.0.8 - 3march2016):
https://github.com/angular/angular/commit/38cb526
Example from repo :
<input [ngControl]="fullName" pattern="[a-zA-Z ]*">
I test it and it works :) - here is my code:
<form (ngSubmit)="onSubmit(room)" #roomForm='ngForm' >
...
<input
id='room-capacity'
type="text"
class="form-control"
[(ngModel)]='room.capacity'
ngControl="capacity"
required
pattern="[0-9]+"
#capacity='ngForm'>
Alternative approach (UPDATE June 2017)
Validation is ONLY on server side. If something is wrong then server return error code e.g HTTP 400 and following json object in response body (as example):
this.err = {
"capacity" : "too_small"
"filed_name" : "error_name",
"field2_name" : "other_error_name",
...
}
In html template I use separate tag (div/span/small etc.)
<input [(ngModel)]='room.capacity' ...>
<small *ngIf="err.capacity" ...>{{ translate(err.capacity) }}</small>
If in 'capacity' is error then tag with msg translation will be visible. This approach have following advantages:
it is very simple
avoid backend validation code duplication on frontend (for regexp validation this can either prevent or complicate ReDoS attacks)
control on way the error is shown (e.g. <small> tag)
backend return error_name which is easy to translate to proper language in frontend
Of course sometimes I make exception if validation is needed on frontend side (e.g. retypePassword field on registration is never send to server).
Since version 2.0.0-beta.8 (2016-03-02), Angular now includes a Validators.pattern regex validator.
See the CHANGELOG
You could build your form using FormBuilder as it let you more flexible way to configure form.
export class MyComp {
form: ControlGroup;
constructor(#Inject()fb: FormBuilder) {
this.form = fb.group({
foo: ['', MyValidators.regex(/^(?!\s|.*\s$).*$/)]
});
}
Then in your template :
<input type="text" ngControl="foo" />
<div *ngIf="!form.foo.valid">Please correct foo entry !</div>
You can also customize ng-invalid CSS class.
As there is actually no validators for regex, you have to write your own. It is a simple function that takes a control in input, and return null if valid or a StringMap if invalid.
export class MyValidators {
static regex(pattern: string): Function {
return (control: Control): {[key: string]: any} => {
return control.value.match(pattern) ? null : {pattern: true};
};
}
}
Hope that it help you.
custom validation step by step
Html template
<form [ngFormModel]="demoForm">
<input
name="NotAllowSpecialCharacters"
type="text"
#demo="ngForm"
[ngFormControl] ="demoForm.controls['spec']"
>
<div class='error' *ngIf="demo.control.touched">
<div *ngIf="demo.control.hasError('required')"> field is required.</div>
<div *ngIf="demo.control.hasError('invalidChar')">Special Characters are not Allowed</div>
</div>
</form>
Component App.ts
import {Control, ControlGroup, FormBuilder, Validators, NgForm, NgClass} from 'angular2/common';
import {CustomValidator} from '../../yourServices/validatorService';
under class
define
demoForm: ControlGroup;
constructor( #Inject(FormBuilder) private Fb: FormBuilder ) {
this.demoForm = Fb.group({
spec: new Control('', Validators.compose([Validators.required, CustomValidator.specialCharValidator])),
})
}
under {../../yourServices/validatorService.ts}
export class CustomValidator {
static specialCharValidator(control: Control): { [key: string]: any } {
if (control.value) {
if (!control.value.match(/[-!$%^&*()_+|~=`{}\[\]:";##'<>?,.\/]/)) {
return null;
}
else {
return { 'invalidChar': true };
}
}
}
}
My solution with Angular 4.0.1: Just showing the UI for required CVC input - where the CVC must be exactly 3 digits:
<form #paymentCardForm="ngForm">
...
<md-input-container align="start">
<input #cvc2="ngModel" mdInput type="text" id="cvc2" name="cvc2" minlength="3" maxlength="3" placeholder="CVC" [(ngModel)]="paymentCard.cvc2" [disabled]="isBusy" pattern="\d{3}" required />
<md-hint *ngIf="cvc2.errors && (cvc2.touched || submitted)" class="validation-result">
<span [hidden]="!cvc2.errors.required && cvc2.dirty">
CVC is required.
</span>
<span [hidden]="!cvc2.errors.minlength && !cvc2.errors.maxlength && !cvc2.errors.pattern">
CVC must be 3 numbers.
</span>
</md-hint>
</md-input-container>
...
<button type="submit" md-raised-button color="primary" (click)="confirm($event, paymentCardForm.value)" [disabled]="isBusy || !paymentCardForm.valid">Confirm</button>
</form>
Without make validation patterns, You can easily trim begin and end spaces using these modules.Try this.
https://www.npmjs.com/package/ngx-trim-directive
https://www.npmjs.com/package/ng2-trim-directive
Thank you.

Resources