Declaring redux form in both the child and parent component causes error - reactjs

Uncaught Error: asyncValidate function passed to reduxForm must return a promise
Declaring multiple redux forms in both child and parent component causes the error above. I feel like it's a bug actually.
I have a parent component called WorkExperience, where I render multiple child components called DashboardListItem, which is the work experience a user has.
In the parent component I have a redux form to create a work experience. Also, I have other redux forms inside the child component where I can toggle edit forms for each list item.
The structure is same as this.
WorkExperience (has postWorkExperienceForm)
DashboardListItem (has edit form with populated initial values)
DashboardListItem
DashboardListItem
So, this structure is causing the error when I type into toggleable edit form. If I remove redux form declaration either from the parent or the child component, everything becomes normal.
Also all the forms are in the store too.
Thank you
Parent Component
renderWorkExperience(){
const workExperience = this.props.candidate.workExperience;
return Object.keys(workExperience).map((key, index) => {
let date = `${workExperience[key].startYear}-${workExperience[key].endYear}`
return <DashboardListItem key={index} {...this.props}
title={workExperience[key].companyName}
subTitle={workExperience[key].title}
meta={date}
summary={workExperience[key].summary}
initialValues={workExperience[key]}
form={workExperience[key]._id} />
});
}
renderForm(){
const activeClass = this.state.displayForm ? 'btn btn-success btn-block mt8' : 'btn btn-primary btn-block mt8'
const { handleSubmit } = this.props;
return(
<form onSubmit={ handleSubmit(this.onSubmit) } className="form-horizontal">
<div className={this.state.displayForm ? 'd-block mb8' : 'd-none'}>
<Field name="companyName"
type="text"
label="Company Name"
placeholder="Apple inc."
id="input-company-name"
component={renderHorizontalTextField} />
<Field name="title"
type="text"
label="Title"
placeholder="Marketing Specialist"
id="input-title"
component={renderHorizontalTextField} />
<Field name="startYear"
type="text"
label="Start Year"
placeholder=""
id="input-start-year"
component={renderHorizontalTextField} />
<Field name="endYear"
type="text"
label="End Year"
placeholder="Blank if current"
id="input-end-year"
component={renderHorizontalTextField} />
<Field name="summary"
rows="4"
label="Summary"
placeholder="Summary..."
id="input-summary"
component={renderTextAreaFieldWithLabelAndPopover} />
</div>
<button type={this.state.displayForm ? "button" : "submit"}
className={activeClass}
onClick={this.handleClick}>{ !this.state.displayForm ?
'Add Work Experience' : 'Save' }
</button>
</form>
)
}
export default reduxForm({
form: 'postWorkExperienceForm'
})(WorkExperience);
Child Component
renderForm(){
const activeClass = this.state.displayForm ? 'btn btn-success btn-block mt8' : 'btn btn-primary btn-block mt8';
const { handleSubmit } = this.props;
return(
<form onSubmit={ handleSubmit(this.onSubmit) } className="form-horizontal">
<div className={this.state.displayForm ? 'd-block mt8' : 'd-none'}>
<Field name="companyName"
type="text"
label="Company Name"
placeholder="Apple inc."
id="input-company-name"
component={renderHorizontalTextField} />
<Field name="title"
type="text"
label="Title"
placeholder="Marketing Specialist"
id="input-title"
component={renderHorizontalTextField} />
<Field name="startYear"
type="text"
label="Start Year"
placeholder=""
id="input-start-year"
component={renderHorizontalTextField} />
<Field name="endYear"
type="text"
label="End Year"
placeholder="Blank if current"
id="input-end-year"
component={renderHorizontalTextField} />
<Field name="summary"
rows="4"
label="Summary"
placeholder="Summary..."
id="input-summary"
component={renderTextAreaFieldWithLabelAndPopover} />
<button className="btn btn-success" type="submit">Save</button>
</div>
</form>
)
}
export default reduxForm({
enableReinitialize: true
})(
connect(null, { updateWorkExperience })(DashboardListItem)
);
Found out that a similar question is asked here. Although he hasn't fixed the problem, found a way around.
Redux Form - "form={ }" and "initialValues={ }" properties not recognized with multiple forms (redux-form v7.0.4)

I ended up putting work experience form into a separate component called WorkExperienceForm and imported it into WorkExperience and DashboardListItem components. This solved the issue.
Form names are passed to WorkExperienceForm from the parent components.
I was planning to put the forms into a separate component anyway, but wanted to solve the problem. So the bug is still there if it is one.
WorkExperienceForm
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { renderHorizontalTextField } from '../Fields/TextFields';
import { renderTextAreaFieldWithLabelAndPopover } from '../Fields/TextAreaFields';
const WorkExperienceForm = ({ handleSubmit, onSubmit }) => {
return(
<form onSubmit={ handleSubmit(onSubmit) } className="form-horizontal">
<Field name="companyName"
type="text"
label="Company Name"
placeholder="Apple inc."
id="input-company-name"
component={renderHorizontalTextField} />
<Field name="title"
type="text"
label="Title"
placeholder="Marketing Specialist"
id="input-title"
component={renderHorizontalTextField} />
<Field name="startYear"
type="text"
label="Start Year"
placeholder=""
id="input-start-year"
component={renderHorizontalTextField} />
<Field name="endYear"
type="text"
label="End Year"
placeholder="Blank if current"
id="input-end-year"
component={renderHorizontalTextField} />
<Field name="summary"
rows="4"
label="Summary"
placeholder="Summary..."
id="input-summary"
component={renderTextAreaFieldWithLabelAndPopover} />
</form>
)
}
export default reduxForm({
enableReinitialize: true
})(WorkExperienceForm);

Related

changing an uncontrolled input of type file in React

i created redux-form to upload form-data with file. but i stumped in how handle the file input, when i tried to select file from form browser console it throw this error
component is changing an uncontrolled input of type file to be controlled. Input elements should not switch from uncontrolled to controlled.......
fileupload.js
import React, { Component } from "react";
import { Field, reduxForm } from "redux-form";
import Card from "#material-ui/core/Card";
class RegisterShop extends Component {
state = {};
renderField(field) {
return (
<div>
<label>{field.label}</label>
<input className="form-control" type={field.type} {...field.input} />
</div>
);
}
onSubmit(values) {
let formData = new FormData();
////
}
render() {
const { handleSubmit } = this.props;
return (
<div>
<Card>
<h1>Register Shop</h1>
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<Field
label="Shop Name"
name="shopname"
type="text"
component={this.renderField}
/>
<Field
label="Describe about your shop"
name="description"
type="text"
component={this.renderField}
/>
<Field
label="Email"
name="email"
type="text"
component={this.renderField}
/>
<Field
label="Contact No"
name="mobileno"
type="text"
component={this.renderField}
/>
<Field
label="Location"
name="locatiion"
type="text"
component={this.renderField}
/>
<Field
label="Shop Logo"
name="image"
type="file"
component="----------" //I changed this many ways still get same error
/>
<button type="submit" className="btn btn-primary">
Login
</button>
</form>
<br />
<br />
</Card>
</div>
);
}
}
export default reduxForm({
form: "registershop"
})(RegisterShop);
I think the problem is here.
<input className="form-control" type={field.type} {...field.input} />
First time, field.input is undefined, so fields is blank object , and input field will like this, which is "an uncontrolled input".
<input>undefined</input>
And after type something in field, the field.input will have value,so be controlled component.
And maybe change to this:
<Field
label="Shop Logo"
name="image"
type="file"
component={MyFile}
dataAllowedFileExtensions="jpg png bmp"></Field>
/>
MyFile component:
class MyFile extends Component {
render() {
const { input,dataAllowedFileExtensions } = this.props
const onInputChange = (e) => {
e.preventDefault();
const files = [...e.target.files];
input.onChange(files);
};
return (
<div>
<input type="file"
onChange={onInputChange}
data-allowed-file-extensions={dataAllowedFileExtensions} />
</div>
)
}
}
Because it’s uncontrolled component, you probably want to leave it as:
<input type=“file”>
Then figure out how to process the input.
From:
React docs
In React, an input type="file" is always an uncontrolled component because its value can only be set by a user, and not programmatically.
You should use the File API to interact with the files. The following example shows how to create a ref to the DOM node to access file(s) in a submit handler:
<input type="file" ref={this.fileInput} />

Is there a way to pass the input fields in redux form to another component?

I want to pass this input value to another component state. To be clear I want the value of this redux form input to pass it to the state of the another component. Is there anyway to achieve it?
renderTextField(field){
return(
<div className="form-group">
<label>{field.label}</label>
<input
className="form-control"
type="text"
placeholder="Name"
{...field.input}
required
/>
</div>
)
}
render(){
const {handleSubmit}=this.props;
return(
<form style={newrow} onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<Field
label={"Student Name"}
name="name"
component={this.renderTextField}
/>
<Field
label={"Average Grade"}
name="avgGrade"
component={this.renderTextField}
/>
<Field
label={"Curriculum / Occupation"}
name="occupation"
component={this.renderTextField}
/>
<button className="btn btn-primary btn-right">Create</button>
</form>
);
}
}
redux-form values are stored in the redux state so they are accessible as any other redux values. They are under form.yourFormName.values.yourInputName (assuming you called your redux-form reducer form)

Redux Form: Getting Access to Field Components meta.touched property inside JSX Form

I am working on a user sign up page and I want to have the user confirm their password after they enter it in. I am trying to access a specific field component's "meta.touched" property from within the form jsx. I have a renderField method that I can use to access it within that function, but I am not able to get to it outside of that. I am trying to have a third field appear after the password field component has been touched. How would I access that?
for example
<Field
label="Please enter an email for you account"
name="email"
type="text"
component={this.renderField}
/>
<Field
label="Please enter a password for your account"
name="password"
type="password"
component={this.renderField}
/>
{if(password.meta.touched == true) &&
<Field
label="Please confirm your password"
name="passwordConfirm"
type="password"
component={this.renderField}
/>}
here is the full component:
import React, {Component} from 'react';
import { Field, reduxForm } from 'redux-form';
import {connect} from 'react-redux';
import * as actions from '../../actions';
import '../../style/signup.css';
class SignUp extends Component {
renderAlert(){
if(this.props.errorMessage){
return(
<div className="alert alert-danger error-message">
<strong>Oops!</strong>
<h6 className="invalid">{this.props.errorMessage}</h6>
</div>
)
}
}
renderField(field){
return(
<div className="form-group">
<label>{field.label}</label>
<input
className="form-control"
type={field.type}
{...field.input}
/>
<div className="invalid">
{field.meta.touched ? field.meta.error: ''}
</div>
</div>
);
}
handleFormSubmit({email, password}){
this.props.signUpUser({email, password});
}
render(){
const {handleSubmit} = this.props;
return(
<div className="container sign-up-form jumbotron">
<h4>Sign Up</h4>
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<Field
label="Please enter an email for you account"
name="email"
type="text"
component={this.renderField}
/>
<Field
label="Please enter a password for your account"
name="password"
type="password"
component={this.renderField}
/>
<Field
label="Please confirm your password"
name="passwordConfirm"
type="password"
component={this.renderField}
/>
{this.renderAlert()}
<button action='submit' className="btn btn-success">
Sign Up <i className="fas fa-user-plus"></i>
</button>
</form>
</div>
)
}
}
function validate(values){
//this function will be called for us
//values is an object that has values user has entered into form
const errors = {};
//if errors has any properties, redux forms assumes
//it is invalid
if(!values.email){
errors.email="Please enter an email to sign up";
}
if(!values.password){
errors.password="Please enter a password to sign up";
}
if(!values.passwordConfirm){
errors.passwordConfirm="Please confirm your password";
}
if(values.password !== values.passwordConfirm){
errors.passwordConfirm="Passwords dont match, please confirm";
}
return errors;
}
function mapStateToProps(state){
return {
errorMessage: state.auth.error
};
}
export default reduxForm({
validate,
form: 'signup'
})(
connect(mapStateToProps, actions)(SignUp)
);
I think the getFormMeta selector will help you here. You can add it to your mapStateToProps:
import { getFormMeta } from 'redux-form';
function mapStateToProps(state) {
return {
errorMessage: state.auth.error,
meta: getFormMeta('signup')(state)
};
}
Docs reference

Submit Redux Form from the Parent Component - Remote Submit

Probably I've checked all the docs and questions out there about this problem but I still couldn't fix it since long time. So, I decided to ask here.
As the title says I am trying to submit a redux form from its parent component. I tried adding hidden submit input, into the form and that works although this isn't the redux form way. What should I do?
Thanks for your help.
WorkExperienceForm.js
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { renderHorizontalTextField } from '../Fields/TextFields';
import { renderTextAreaFieldWithLabelAndPopover } from '../Fields/TextAreaFields';
const WorkExperienceForm = ({ handleSubmit, onSubmit, shouldHide, form }) => {
if(shouldHide) return false;
return(
<form onSubmit={ handleSubmit(onSubmit) } className="form-horizontal">
<Field name="companyName"
type="text"
label="Company Name"
placeholder="Apple inc."
id="input-company-name"
component={renderHorizontalTextField} />
<Field name="title"
type="text"
label="Title"
placeholder="Marketing Specialist"
id="input-title"
component={renderHorizontalTextField} />
<Field name="startYear"
type="text"
label="Start Year"
placeholder=""
id="input-start-year"
component={renderHorizontalTextField} />
<Field name="endYear"
type="text"
label="End Year"
placeholder="Blank if current"
id="input-end-year"
component={renderHorizontalTextField} />
<Field name="summary"
rows="4"
label="Summary"
placeholder="Summary..."
id="input-summary"
component={renderTextAreaFieldWithLabelAndPopover} />
</form>
)
}
export default reduxForm({
enableReinitialize: true
})(WorkExperienceForm);
This is the parent component
onSubmit(values){
this.props.createWorkExperience(values, () => {
this.props.notify();
})
}
render(){
if(!this.props.candidate || !this.props.candidate['workExperience'].length > 0) return this.renderEmptyState();
const activeClass = this.state.displayForm ? 'btn btn-success btn-block mt8' : 'btn btn-primary btn-block mt8'
return(
<div>
{ this.renderWorkExperience() }
<WorkExperienceForm {...this.props}
form='postWorkExperienceForm'
onSubmit={this.onSubmit}
shouldHide={!this.state.displayForm} />
<button type={ this.state.displayForm ? 'submit' : 'button' }
htmlFor='postWorkExperienceForm'
className={activeClass} >{ this.state.displayForm ? 'Save' : 'Add Work Experience' }
</button>
</div>
)
}
Yeah, the documentation is sparse regarding this matter.
I think you may have Context issues. I think you should move the button to the Form Component and pass the mySubmit (renamed it from onSubmit for clarity) function to the Form Component with an arrow function via onSubmit property.
mySubmit(formValues){
this.props.createWorkExperience(formValues, () => {
this.props.notify();
})
}
<WorkExperienceForm {...this.props}
form='postWorkExperienceForm'
onSubmit={(formValues) => this.mySubmit(formValues)}
shouldHide={!this.state.displayForm} />
Then in the Form Component wrap the onSubmit in the handleSubmit function.
<form onSubmit={handleSubmit(onSubmit)}>
Here is a example where we recently had to do this with Redux-Form.
Maybe, it will help you some.
Note, we are using FLOW.
Parent Component: beer.add.component.js
Form Component: beer.add.form.component.js
Live production example, so you know that it works.
Note, The SignInForm uses the same pattern.

How can I pass the input id with the name and value in redux form?

I'm following along with one of redux-form's tutorials and I'm unable to find a reference on how to properly pass an input's id when submitting the form. Currently, the key value pair submits just fine, but if I want to add the id per input as well, how can I achieve that?
demo
const renderField = (props) => (
<div>
<label>{props.label}</label>
<div>
<input {...props.input} placeholder={props.label} type={props.type} id={props.id}/>
{props.meta.touched && ((props.meta.error && <span>
{props.meta.error}</span>) || (props.meta.warning && <span>
{props.meta.warning}</span>))}
</div>
</div>
)
const FieldLevelValidationForm = (props) => {
const { handleSubmit, pristine, reset, submitting } = props
return (
<form onSubmit={() => handleSubmit(this, props.id)}>
<Field name="username" type="text"
component={renderField} label="Username"
id="user"
validate={[ required, maxLength15 ]}
/>
<Field name="email" type="email"
component={renderField} label="Email"
id="userEmail"
validate={email}
warn={aol}
/>
<Field name="age" type="number"
component={renderField} label="Age"
id="userAge"
validate={[ required, number ]}
warn={tooOld}
/>
<Field name="favoriteColor" component="select" id="userColor">
<option value="ff0000">Red</option>
<option value="00ff00">Green</option>
<option value="0000ff">Blue</option>
</Field>
<div>
<button type="submit" disabled={submitting}>Submit</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
</div>
</form>
)
}

Resources