below is the code for the redux-form. Everything works beautifully with the redux store but I can not display the error messages in the span element.
import React from 'react'
import { Button } from 'react-bootstrap'
import { Field, reduxForm } from 'redux-form'
const validate = (values) => {
const errors = {}
if (!values.firstname) {
errors.firstname = 'Required'
}
return errors
}
const renderInput = (field) => (
<div>
<label>{field.placeholder}</label>
<div>
<input {...field.input}/>
{field.error && <span>{field.error}</span>}
</div>
</div>
)
#reduxForm({
form: 'addUserForm',
validate
})
export default class CreateUserForm extends React.Component {
render() {
const {addUser, handleSubmit} = this.props
return (
<form onSubmit={handleSubmit(addUser)}>
<Field type="text" placeholder="First name" component={renderInput} name="firstname" />
<Button type="submit" className="btn btn-success">Submit</Button>
</form>
)
}
}
I can clearly see that the validation function works (see screen shot below)
but there is nothing in the <span></span> element, which means the field.error has no value. I also don't get an error message at all.
Does someone know what's going on here?
Thanks,
Thomas
Your renderInput is incomplete.
The official document shows:
const renderField = ({ input, label, type, meta: { touched, error, warning } }) => (
<div>
<label>{label}</label>
<input {...input} placeholder={label} type={type}/>
{
touched && (
(error && <span>{error}</span>) || (warning && <span>{warning}</span>)
)
}
</div>
)
Observe the object, parameters passed to renderField: meta: { touched, error, warning }
With that regards, shouldn't your renderInput be:
const renderInput = (field) => (
<div>
<label>{field.placeholder}</label>
<div>
<input {...field.input}/>
{field.meta.error && <span>{field.meta.error}</span>}
</div>
</div>
)
Missing => field.meta.error
Related
I have redux form in a class component and for some reason when I console log the formValues I am getting undefined what is the problem?
class CreateTeamBox extends Component{
handleFormSubmit({name}){
console.log(name);
}
renderError = ({ touched, error}) => {
if(touched && error) {
return(
<div>{error}</div>
);
}
}
renderInput = ({input, label, type, meta}) => {
return(
<div className={styles.formGroup}>
<label>{label}</label>
<input {...input} />
<div className={styles.errorWrapper}>
{this.renderError(meta)}
</div>
</div>
);
}
render() {
const {handleSubmit, error} = this.props ;
return(
<div className={styles.createTeamBox}>
<div className={styles.titleWrapper}>
<h2>create team</h2>
</div>
<div className={styles.bodyWrapper}>
<div className={styles.submitErrorWrapper}>
{error ? <Error error={error} /> : null}
</div>
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<Field name="name" component={this.renderInput} label="name" />
<button className={styles.button} type="submit" >create</button>
</form>
</div>
</div>
)
}
}
const validate = (formValues) => {
console.log(formValues.name);
const name = formValues.name;
const errors = validateForm(name);
return errors;
}
export default reduxForm({
form: 'createTeamForm',
validate
})(CreateTeamBox);
I have other reduxform in the same given project with different names, is it causing the problem? Im not sure why it is happening as I havecopied most of the coe from working reduxform in the given project .
the default value of formValues will be empty object, after enter some data to the field then you can get the value, or you can pass the value by using initialValues
so I'm trying to separate redux-form to stateless-component and container-component, but when I'm trying to do syncValidation, for some reason there is no validation called. what am I missing?
the error and warning in the renderField returns undefined.
and I'm planning to move the renderField function from the stateless component
container -
let EditMovie = (props) => {
const { updateMovie, editModal, editModalStateChange, invalid, initialValues, handleSubmit, pristine } = props;
return (
<MovieModal
modalTitle={initialValues.Title}
initialValues= {initialValues}
invalid= {invalid}
validators= {Validators}
exit= {editModalStateChange}
isOpen= {editModal}
handleSubmit= {handleSubmit}
onSubmit= {updateMovie}
pristine={pristine}
/>
);
};
const validate = values => {
const errors = {}
if (!values.Title) {
errors.username = 'Required'
}
return errors
}
const warn = values => {
const warnings = {
Title: 'bla bla'
}
return warnings
}
const mapStateToProps = (state) => ({
initialValues: state.movies.selectedMovie,
editModal: state.movies.editModal,
});
EditMovie = reduxForm({ form: 'editMovie', validate, warn })(EditMovie);
export default connect(mapStateToProps, { editModalStateChange, updateMovie } )(EditMovie);
stateless -
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>
)
const MovieModal = (props) => {
const { pristine, handleSubmit, onSubmit, isOpen, exit, validators, invalid, modalTitle } = props;
return (
<Modal visible={isOpen} onClickBackdrop={() => exit()}>
<div className="modal-body">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-group">
<Field component={renderField} name="Title" label="Movie Title" />
</div>
<div className="modal-footer">
<button type="button" className="btn btn-secondary" onClick={() => exit()}>close</button>
<button type="submit" className="btn btn-primary" disabled={invalid || pristine}>Save</button>
</div>
</form>
</div>
</Modal>
);
}
you need to provide validate function in Field Component
Well I Found the problem !
the issue was with the Bootstrap Modal module named - react-bootstrap4-modal
when i wrapped MovieModal in the Modal Class and not inside of it, everything worked fine
thanks for the help
I am unable to populate the form fields with the supplied initial values. Got
stuck.
I am using "react": "^16.2.0" and redux-form": "7.4.0". Please help me on where I got stuck in the code. Even I tired hardcoded the initialValues but still no luck.
Can somebody please help me?
This is my code. Spent most of the time for this.
import React from 'react'
import { connect } from 'react-redux'
import { Field, reduxForm } from 'redux-form'
import { SubmissionError } from 'redux-form'
import { addStore } from "../actions/storeActions.js";
import { loadStore } from "../actions/storeActions.js";
//import submit from './submit'
import './bundle.css';
const required = value => (value ? undefined : 'Required')
const phoneNumber = value =>
value && !/^(0|[1-9][0-9]{9})$/i.test(value)
? 'Invalid phone number, must be 10 digits'
: undefined
const number = value =>
value && isNaN(Number(value)) ? 'Must be a number' : undefined
const renderField = ({ input, label, type, value, meta: { touched, error } }) => (
<div>
<label>{label}</label>
<div>
<input {...input} placeholder={label} type={type} value={value} />
{touched && error && <span>{error}</span>}
</div>
</div>
)
function throwError(msg) {
throw new SubmissionError({
_error: msg
})
}
function submit(values, props) {
console.log(values, props);
let msg;
if ((msg = required(values.storeName)) !== undefined) {
throwError("Store name is required")
} else if ((msg = required(values.address)) !== undefined) {
throwError("Address is required")
} else if ((msg = required(values.phone)) !== undefined) {
throwError("Phone number is required")
} else if ((msg = phoneNumber(values.phone)) !== undefined) {
throwError(msg)
} else {
props.dispatch(addStore(values, props.router));
}
//})
}
let StoreForm = props => {
const { error, handleSubmit, pristine, reset, submitting, } = props
return (
<form onSubmit={handleSubmit(values => { submit(values, props) })}>
<Field
name="storeName"
component={renderField}
type="text"
placeholder="Store Name"
label="Store Name"
/>
<Field
name="address"
component={renderField}
type="text"
placeholder="Address"
label="Address"
/>
<Field
name="description"
component={renderField}
type="text"
placeholder="Description"
label="Description"
/>
<Field
name="phone"
component={renderField}
type="text"
placeholder="Phone"
label="Phone"
/>
{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>
)
}
const mapDispatchToProps = dispatch => {
return {
initialValues: () => dispatch(loadStore())
};
};
const mapStateToProps = (state) => {
return {
initialValues: state.storeReducer.items
}
};
StoreForm = reduxForm({
form: 'initializeFromState' // a unique identifier for this form
})(StoreForm)
StoreForm = connect(
state => ({
initialValues: { storeName: "SURSH" },
})
)(StoreForm)
export default StoreForm
you havn't pass value in your Field components
It's caused by wrong usage of value in your renderField function.
As you can see in the official props docs, the value is part of the input property.
You have to refactor the function in the following way:
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>
)
i am using redux form and what i do here is when user selects category the next select field should have all subcategories depending of category selected.
what i did was i created api for fetching all categories, i trigger action via componentWillMount and load all categories in first categories select field, then i use formValueSelector of redux-form to get selected category to the state/this.props, then i use componentWillReceiveProps() to trigger fetching subcategories with that e.g. "this.props.categoryId" that i put to state with formValueSelector and that works.
and my question is, is this the right aporoach, is there a better way?
and the second question, is how do i reset categoryChildId field to, lets say blank when the categoryId field is changed?
import React from 'react';
import moment from 'moment';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { connect } from 'react-redux';
import { Link, NavLink } from 'react-router-dom';
import {CopyToClipboard} from 'react-copy-to-clipboard';
import * as actions from '../../actions/category';
const renderField = ({ input, label, type, meta: { touched, error } }) => (
<div>
<input {...input} placeholder={label} type={type} />
{touched &&
error &&
<div className="error">{error}</div>}
</div>
)
const renderTextArea = ({ input, label, type, meta: { touched, error } }) => (
<div>
<textarea {...input} placeholder={label} type={type} />
{touched &&
error &&
<div className="error">{error}</div>}
</div>
)
class AddProduct extends React.Component {
constructor(props) {
super(props);
this.state = {
value: `${process.env.SITE_URL}/user/${props.user.username}`,
copied: false,
isLoading: false
};
}
componentWillMount() {
this.props.startSetCategories()
}
componentWillReceiveProps(nextProps) {
this.props.startSetCategoryChildren(nextProps.categoryId)
console.log(nextProps)
}
renderCategorySelector = ({ input, meta: { touched, error } }) => {
return (
<div>
<select {...input}>
<option value="">select category</option>
{!this.props.categories ? (
<option value="">loading...</option>
) : (
this.props.categories.map(category => <option value={category._id} key={category._id}>{category.name}</option>)
)
}
</select>
{touched && error && <span>{error}</span>}
</div>
)
}
renderCategoryChildSelector = ({ input, meta: { touched, error } }) => {
return (
<div>
<select {...input}>
<option value="">select sub category</option>
{!this.props.categoryChildren ? (
<option value="">loading...</option>
) : (
this.props.categoryChildren.categoryChildren.map(categoryChild => <option value={categoryChild._id} key={categoryChild._id}>{categoryChild.name}</option>)
)
}
</select>
{touched && error && <span>{error}</span>}
</div>
)
}
submitForm = values => {
console.log(values)
}
render() {
const username = localStorage.getItem('username');
const { user } = this.props;
const { handleSubmit, pristine, submitting, categoryId } = this.props;
return (
<div className="profile-wrapper">
<div className="profile">
<form className="profile-addproduct-left" onSubmit={handleSubmit(this.submitForm.bind(this))}>
<div className="profile-addproduct-title">
<h2>New Product</h2>
<p>Fill out the form.</p>
</div>
<div className="profile-form-group">
<div className="profile-form-item">
<p>Title</p>
<Field
name="title"
type="text"
label="title of a product"
component={renderField}
/>
</div>
<div className="profile-form-item">
<p>Category</p>
<Field
name="categoryId"
type="text"
component={this.renderCategorySelector}
label="category"
/>
{this.props.categoryId ?
<Field
name="categoryChildId"
type="text"
component={this.renderCategoryChildSelector}
label="categoryChild"
/> :
''
}
</div>
<div className="profile-form-item">
<p>Description</p>
<Field
name="description"
type="text"
label="Write some interesting..."
component={renderTextArea}
/>
</div>
</div>
<div className="profile-addproduct-form-submit">
<button className="button button--register" type="submit" disabled={this.state.isLoading || pristine}>Submit New Product</button>
</div>
</form>
</div>
</div>
)
}
};
AddProduct = reduxForm({
form: 'addproduct-form'
})(AddProduct)
const selector = formValueSelector('addproduct-form')
AddProduct = connect(state => {
const categoryId = selector(state, 'categoryId')
return {
categoryId,
categories: state.category.categories,
categoryChildren: state.category.categoryChildren
}
}, actions)(AddProduct)
export default AddProduct
You should not call the startSetCategoryChildren (or any other api) in componentWillReceiveProps... because it would call every time whenever componentWillReceiveProps called
componentWillReceiveProps(nextProps) {
this.props.startSetCategoryChildren(nextProps.categoryId)
console.log(nextProps)
}
Instead of this you can to do this on handleChange of the Field
<Field
name="categoryId"
type="text"
component={this.renderCategorySelector}
label="category"
onChange={(e) => this.props.startSetCategoryChildren(e.target.value)}
/>
I have a contact page on which I have a contact form defined like this:
import React from "react";
import { Field, reduxForm } from "redux-form";
import Recaptcha from "react-recaptcha";
const required = value => (value ? undefined : "This field is required.");
const email = value => value && !/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value) ? "Invalid email address." : undefined;
const renderInput = ({
input,
label,
type,
meta: { touched, error }
}) => (
<div className="form-group">
<label className="col-sm-2 control-label">{ label }</label>
<div className="col-sm-10">
{ (type == "text" || type == "email") ? <input { ...input } type={ type } /> : <textarea { ...input }></textarea> }
{ touched && ((error && <span className="contact-form-error-message">{ error }</span>)) }
</div>
</div>
);
const captcha = (props) => (
<div className="form-group">
<label className="col-sm-2 control-label"></label>
<div className="col-sm-10">
<Recaptcha render="explicit" onloadCallback={ console.log.bind(this, "reCAPTCHA loaded.") }
sitekey="XXXXXXXXXXXXXXXXX" onChange={props.input.onChange} />
</div>
</div>
);
const ContactForm = props => {
const { handleSubmit, submitting } = props
return (
<form className="form-horizontal" onSubmit={ handleSubmit }>
<Field
name="name"
type="text"
component={ renderInput }
label="Name:"
validate={ required }
/>
<Field
name="email"
type="email"
component={ renderInput }
label="Email:"
validate={ [required, email] }
/>
<Field
name="subject"
type="text"
component={ renderInput }
label="Subject:"
validate={ required }
/>
<Field
name="message"
type="textarea"
component={ renderInput }
label="Message:"
validate={ required }
/>
<Field name="recaptchacode" component={ captcha } />
<div className="form-group">
<label className="col-sm-2 control-label"></label>
<div className="col-sm-10">
<button type="submit" id="contact-form-button" disabled={ submitting }>Send</button>
</div>
</div>
</form>
)
}
export default reduxForm({
form: "ContactForm"
})(ContactForm);
The problem is I cannot seem to get the recaptchacode field in the values object when I click the submit button. How do I bind the value of the Recaptcha component to redux-form so that it puts it in the values object?
And since StackOverflow wants me to add more explanation to this because there's too much code, I am writing this text.
So the answer in short as I have managed to get this thing working. There are two npm packages for implementing recaptcha in react:
react-recaptcha and react-google-recaptcha. You want the second one and not the first one (which was my problem and doesn't work with redux-form) and then you want to follow this tutorial: https://github.com/erikras/redux-form/issues/1880
Hope this helps someone.
Here’s how I integrated Google ReCaptcha with React and redux-forms with Language support. Hope this will help someone.
Versions:
React: 16.5.2
react-google-recaptcha: 1.0.5
react-redux: 5.0.6
redux: 3.7.2
redux-form: 7.2.0
Redux form:
import React from 'react';
import {
reduxForm,
Field,
formValueSelector,
change,
} from 'redux-form';
import { testAction } from ‘/actions';
import { connect } from 'react-redux';
import Captcha from './Captcha';
const validate = values => {
const errors = {};
if (!values.captchaResponse) {
errors.captchaResponse = 'Please validate the captcha.';
}
return errors;
};
let TestForm = (props) => {
const {
handleSubmit,
testAction,
language, //extract language from props/or hard code it in Captcha component
} = props;
return (
<Form onSubmit={ handleSubmit(testAction)}>
<Field component={Input} name=“other_input_name” type="text" required/>
<Field dispatch={props.dispatch} component={Captcha} change={change} language={language} name="captchaResponse"/> {/* Pass redux-forms change and language to the Captcha component*/}
<Button type="submit">{‘Validate’}</Button>
</Form>
);
};
const selector = formValueSelector('testForm');
TestForm = connect(
state => ({
recaptchaValue: selector(state, 'captchaResponse'),
}),
{ testAction: testAction }
)(TestForm);
export default reduxForm({
form: ‘testForm’,
validate,
enableReinitialize: true,
})(TestForm);
Captcha component:
import React, { Component } from 'react';
import ReCAPTCHA from 'react-google-recaptcha';
import styled from 'styled-components';
import { change } from 'redux-form';
class Captcha extends Component {
constructor(props) {
super(props);
window.recaptchaOptions = { lang: this.props.language }; //set language that comes from props E.g.: fr/es/en etc..
}
onChange = (value) => {
this.props.meta.dispatch(change(‘testForm’, 'captchaResponse', value));
};
render() {
const { meta: { touched, error } } = this.props;
return (
<CaptchaWrapper>
<ReCAPTCHA
sitekey={‘xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx’}
onChange={response => this.onChange(response)}
/>
<ErrorMessage>{ touched ? error : '' }</ErrorMessage>
</CaptchaWrapper>
);
}
}
const CaptchaWrapper = styled.div`
`;
const ErrorMessage = styled.p`
color: red;
`;
export default Captcha;