I am working with Redux Form and in that I am trying to set the value of Hidden Input field in a loop. But the value doesn't get submitted as the field was never touched. Is there a way to solve this or am I missing something?
const renderEmployeeFields = (fields) => {
return fields.map((e, i) => {
return (
<Field
key={e._id}
name={`employeeSchedule[${i}].employee`}
component={renderTextField}
employeeId={e._id}
label={e._id}
/>
)
})
}
const renderTextField = ({ input, label, employeeId}, autocompleteKey) =>
<Input key={`${label}-input`} autoComplete={`${autocompleteKey}-${label}`} autoFocus {...input} value={employeeId} type='hidden'/>
You have to set the value using the initialValues property (https://redux-form.com/7.4.2/docs/api/reduxform.md/#-code-initialvalues-object-lt-string-string-gt-code-optional-) so that they will be available on submit.
You don't need then the hidden input.
Related
I have a React Redux Form which has a few required fields and has its initial values set with initialValues prop
The values initialize fine, but when I try to save the form it errors saying the field is required (even though there's a value in there). If I simply CLICK into the field then save again everything works fine!
I have tried every way I can find using initialize/reset/destroy/change/blur/etc to manually touch or set the field all to no avail
reduxForm({
form: 'formName',
touchOnChange: true,
touchOnBlur: true
}),
useEffect(() => {
if (initialValues && initialValues.field) {
change(field, value)
blur(field, value)
}
}, [initialValues])
and a whole slew of different options as above
Same behavior if I try to reset and re-init the form, or call change. If I just click into the field though then the validation passes as expected.
Also tried enableReinitialize: true but that didn't change the behavior either
initialValues is set via an async call which updates redux state var, I'm guessing this is the issue at hand. I've been unable to reproduce with any of the mvp sandbox examples.
The values are getting set just fine in the fields but it's like the validators just aren't checking the field after the initialValue is set unless the user performs a mouse click in them.
How can I tell the form there's already a value in there just check, without the user manually clicking into the field
-- some new info
If I manually touch and then blur the field ... the validation fails immediately instead of waiting for submit to be pressed, so it really must think there's no value in the input until there's a mouse click there
It looks like you are in need of a form validation. The below is a example form / form validation from https://redux-form.com/6.0.1/examples/submitvalidation/.
submit.js
import { SubmissionError } from 'redux-form'
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
function submit(values) {
return sleep(1000) // simulate server latency
.then(() => {
if (![ 'john', 'paul', 'george', 'ringo' ].includes(values.username)) {
throw new SubmissionError({ username: 'User does not exist', _error: 'Login failed!' })
} else if (values.password !== 'redux-form') {
throw new SubmissionError({ password: 'Wrong password', _error: 'Login failed!' })
} else {
window.alert(`You submitted:\n\n${JSON.stringify(values, null, 2)}`)
}
})
}
export default submit
SubmitValidationForm.js
import React from 'react'
import { Field, reduxForm } from 'redux-form'
import submit from './submit'
const renderField = ({ input, label, type, meta: { touched, error } }) => (
<div>
<label>{label}</label>
<div>
<input {...input} placeholder={label} type={type}/>
{touched && error && <span>{error}</span>}
</div>
</div>
)
const SubmitValidationForm = (props) => {
const { error, handleSubmit, pristine, reset, submitting } = props
return (
<form onSubmit={handleSubmit(submit)}>
<Field name="username" type="text" component={renderField} label="Username"/>
<Field name="password" type="password" component={renderField} label="Password"/>
{error && <strong>{error}</strong>}
<div>
<button type="submit" disabled={submitting}>Log In</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
</div>
</form>
)
}
export default reduxForm({
form: 'submitValidation' // a unique identifier for this form
})(SubmitValidationForm)
Good luck!
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.
In my project we are building a form with React and Redux-Form. We have a single information that is composed by the value of two inputs. But the values of each input is combined and validated together.
The first implementation try was by connecting each component with Field. It let us update the state properly but we couldn't validate all the values together with the validate prop.
The second Try was using Fieldscomponent but it does not have a validate prop. We thought in create a Pull Request for it but the API for it isn't clear yet, since what we want to validate the combination of the two values and the behavior of the Fields props (such as parse and format) is different, executing the function for each input inside Fields component separately.
I know it is possible to create a component and use Field to connect with the application state, but I didn't want to manage things as the touched prop, or the callbacks to update the state, or other things that I even have noticed, since Redux-Form has all of it done.
The fact is that I end up with an implementation but it didn't looked very elegant. I'd like you to take a look at the implementation and give your opinion, sugest other solutions and even if this solution is not implemented in Redux-Form yet we could maybe open a pull request for that.
Here is an example implementation
Simple form container
import SimpleForm from 'app/simpleForm/components/simpleForm'
import { reduxForm } from 'redux-form'
export default reduxForm({
form: 'simpleForm'
})(SimpleForm)
Simple form component
import React from 'react'
import { Field } from 'redux-form'
import MultiInputText from 'app/simpleForm/components/multiInputText'
const onSubmit = values => alert(JSON.stringify(values))
const validateAddress = (value) => {
if (!value) return 'Address is empty'
if (!value.street) return 'Street is empty'
if (!value.number) return 'Number is empty'
return null
}
const SimpleForm = ({ handleSubmit }) => {
return (
<form onSubmit={ handleSubmit(onSubmit) }>
<Field label="Home Address" name="home" component={MultiInputText} type="text" validate={validateAddress}/>
<Field label="Work Address" name="work" component={MultiInputText} type="text" validate={validateAddress}/>
<button type="submit">Submit</button>
</form>
)
}
export default SimpleForm
MultiInputText component
import React from 'react'
import { Fields, FormSection } from 'redux-form'
const renderAddreessInputs = ({ street, number, error }) => (<div>
<input {...street.input} type="text" />
<input {...number.input} type="text" />
{ street.meta.touched && number.meta.touched && error && <span className="error">{error}</span> }
</div>)
const MultiInputText = (props) => {
const { input: { name }, label, meta: { error }} = props
const names = [
'street',
'number'
]
return (<div>
<label htmlFor={name}>{label}</label>
<FormSection name={name}>
<Fields names={names} component={renderAddreessInputs} error={error}/>
</FormSection>
</div>)
}
export default MultiInputText
I see two options:
1) Use record-level validation.
reduxForm({
form: 'addressForm',
validate: values => {
const errors = {}
if(!home) {
errors.home = 'Address is empty'
}
// etc, etc. Could reuse same code for home and work
return errors
}
})
2) Create a single input that handles a complex value.
<Field name="home" component="AddressInput"/>
...
const AddressInput = ({ input, meta }) =>
<div>
<input
name={`${input.name}.street`}
value={(input.value && input.value.street) || ''}
onChange={event => input.onChange({
...input.value,
street: event.target.value
})}/>
...other inputs here...
</div>
That's total pseudocode, but I hope it gets the point across: a single input can edit a whole object structure.
Personally, I'd choose Option 1, but I prefer record-level validation over field-level validation in general. The nice thing about Option 2 is that a single AddressInput could be reused across the application. The downside is that you don't get specific field-level focus/blur/dirty/pristine state.
Hope that helps...?
I have a FieldArray like this:
renderLanguages = fields => (
<div>
{fields.map(fieldName => <Field name={fieldName + '.iso'} component="input" type="text" />
</div>
)
<FieldArray name="languages" component={renderLanguages} />
And i like to validate it in a async way:
const asyncValidate = values => {
console.log(values);
}
export default reduxForm({
form: 'languagesForm',
asyncValidate,
asyncBlurFields: ['languages']
})(LanguagesForm)
My asyncValidate never gets called. I wonder if i have to specify the asyncBlur fields in an other way. Or if redux-form does not provide the async validation of field arrays.
To get Redux-form FormArray working with the asyncValidate, it requires to pass the field as languages[].<nameOfTheFieldOfChildComponet> but not languages only.
I think its good design too as generally, we don't validate asynchronously on change of any parameter but a specific parameter.
Here is the working example:- https://codesandbox.io/s/mq8zz58mrj
const asyncValidate = values => {
console.log(values);
}
export default reduxForm({
form: 'languagesForm',
asyncValidate,
asyncBlurFields: ['languages[].name']
})(LanguagesForm)
I am using Redux Form version 6.4.3 and I'm trying to validate two date fields such that the 'to' date must always be before the 'from' date.
Other examples say I ought to be able to refer to the fields array in props but there is no such array. The form state has an array called registeredFields however but those just seem to be of the form {name: 'dob', type: 'Field'}
Here is my form code
import React from 'react'
import DatePicker from 'react-bootstrap-date-picker'
import moment from 'moment'
import {Field, reduxForm} from 'redux-form'
import {Form, Row, Col, Button, FormGroup, ControlLabel, FormControl, HelpBlock} from 'react-bootstrap'
// validations
const required = value => !value ? 'This field is required' : undefined
const maxDate = max => value =>
value && moment(value).isAfter(max) ? `Must be before ${max}` : undefined
const minDate = min => value =>
value && moment(value).isBefore(min) ? `Must be after ${min}` : undefined
const renderDatepicker = ({ input, label, hint, showTodayButton, meta: { pristine, touched, warning, error } }) => {
const validationState = pristine ? null : error ? 'error' : warning ? 'warning' : null
return (
<FormGroup validationState={validationState}>
<Col componentClass={ControlLabel} sm={3}>{label}</Col>
<Col sm={3}>
<FormControl
{...input}
componentClass={DatePicker}
placeholder="DD/MM/YYYY"
dateFormat="DD/MM/YYYY"
showTodayButton={showTodayButton}/>
</Col>
{pristine && !!hint && (
<Col sm={6}>
<HelpBlock>{hint}</HelpBlock>
</Col>
)}
{touched && (
(error && (
<Col sm={6}>
<HelpBlock>{error}</HelpBlock>
</Col>)
) || (warning && (
<Col sm={6}>
<HelpBlock>{warning}</HelpBlock>
</Col>
))
)}
</FormGroup>
)}
const MyForm = props => {
const { error, handleSubmit, pristine, reset, submitting, fields } = props
console.debug('fields', fields) // fields is undefined
return (
<Form horizontal>
<Field
name="dateFrom"
component={renderDatepicker}
label="Date from"
hint="Earliest date for enquiry"
validate={[required, maxDate('where do I get the other date value from?')]}
/>
<Field
name="dateTo"
component={renderDatepicker}
label="Date to"
showTodayButton={true}
hint="Latest date for enquiry"
validate={[required, minDate('where do I get the other date value from?')]}
/>
</Form>
)
}
export default reduxForm({
form: 'MyForm',
})(MyForm)
I get the feeling I am missing something obvious since all the examples I have seen expect that the fields array to exist in the props.
It's also worth mentioning that the Field's validate function signature is validate : (value, allValues, props) => error [optional] so you actually have a reference to other fields values in field-level validation as well.
I believe you could do something like:
const maxDateValidationAdapter = (value, values) => maxDate(values.dateTo)(value);
// Alternatively, if maxDate() is used only in this form you can just change its signature
<Field
name="dateFrom"
component={renderDatepicker}
label="Date from"
hint="Earliest date for enquiry"
validate={[required, maxDateValidationAdapter]}
/>
See Field documentation (Props you can pass to Field => validate).
For Redux Form, use:
export default reduxForm({
form: 'MyForm', // a unique identifier for this form
validate, // <--- validation function given to redux-form
})(MyForm)
const validate = values => {
const errors = {}
// Here you can get all the fields in value object, use value.min or value.max
return errors
}
http://redux-form.com/6.4.3/examples/syncValidation/