Submit Redux Form from the Parent Component - Remote Submit - reactjs

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.

Related

Redux form cannot type

I cannot type in the text input of redux form.
it's a very minimal form
function Login({ handleSubmit, handleChange }) {
const [username, setUsername] = useState(undefined);
const [password, setPassword] = useState(undefined);
const onSubmit = (e) => {
console.log(e);
console.log(username);
console.log(password);
};
console.log(handleSubmit);
return (
<Container>
<div className={styles.centered}>
<div className={styles.form}>
<div className={styles.title}>
<H3>Login</H3>
</div>
<form onSubmit={() => handleSubmit(onSubmit)} className={styles.flexColumn}>
<div className={styles.username}>
<P>username</P>
<Field name="username" component="input" type="text" className={styles.input} />
</div>
<div className={styles.password}>
<P>password</P>
<Field name="password" component="input" type="password" className={styles.input} />
</div>
<div className={styles.downSection}>
<Flex>
<P>
Serve Aiuto?
</P>
<a href="#">
<div className={styles.contactLink}>
<P>Contattaci</P>
</div>
</a>
</Flex>
<Button type="submit" text="Accedi" />
</div>
</form>
</div>
</div>
</Container>
);
}
const mapDispatchToProps = {
login: loginAction,
};
const enhance = compose(
connect(null, mapDispatchToProps),
reduxForm({ form: 'login' }),
);
export default enhance(Login);
The handleSubmit doesn't work, i cannot console.log anything.
I tried to see the documentation and tried to search some answer on SO but i didn't find an answer.
Could you please tell me where is the error ? thanks.
So give this a try, let's leave enhance out, I don't know what it does honestly so let's try this type of Login configuration where we turn the component into a class-based one which is good practice anyway since you are receiving inputs from a user.
I do realize you are using useState which is some of the cool new features with React, but what I am recommending is to put together a less complex and conventional setup with a class-based component like so:
import React, { Component } from "react";
import { reduxForm, Field } from "redux-form";
class Login extends Component {
render() {
return (
<form>
<fieldset>
<label>Email</label>
<Field
name="email"
type="text"
component="input"
/>
</fieldset>
<fieldset>
<label>Password</label>
<Field
name="password"
type="password"
component="input"
/>
</fieldset>
</form>
);
}
}
export default reduxForm({ form: "login" })(Login);
Use this to check to see if you can now type into your inputs and then start adding stuff back in and test it every single time until you find the cause of the problem.
Try first just to handle the event
<form onSubmit={onSubmit} className={styles.flexColumn}>
after that try using the this in the function onsubmit and remove the const
onSubmit(event){
console.log(e);
console.log(username);
console.log(password);
this.handleSubmit(event.target.value);
};
after several hours and a special night of bug fixing i discovered the problem:
it was in one import, exactly:
import { Field, reduxForm } from 'redux-form/immutable';
and not
import { Field, reduxForm } from 'redux-form';
this was completely unexpected, i was pretty sure that the mistake was in the body of the component, not in the import.
the structure of the file was ok.

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} />

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

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

how to create re-usable field component in redux-form?

I have 2-3 fields that is reused is other forms of my application. So i wanted to create those fields as component so that I can resuse is my other forms. but redux-form is complaining that
Error: Field must be inside a component decorated with reduxForm()
Any ideas how can I achieve it? BTW, I am using material-ui
EDIT: providing a better e.g. consider the material-ui toolbar
http://www.material-ui.com/#/components/toolbar
My toolbar consists of a selectField, textField, Toggle button, which may couple of forms. In my app, I want to keep this toolbar in all the forms where I create objects in my application, so I want to include this toolbar in all the forms. After the below answer I tried something dirty like below.
class BaseBar extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
value: 3,
isGlueOpen: false,
};
}
handleChange = (event, index, value) => {
this.setState({value: value});
}
render() {
return (
<div>
<Toolbar>
<ToolbarGroup firstChild={true}>
<DropDownMenu value={this.state.value} onChange={this.handleChange}>
<MenuItem value={1} primaryText="All Broadcasts" />
<MenuItem value={2} primaryText="All Voice" />
<MenuItem value={3} primaryText="All Text" />
<MenuItem value={4} primaryText="Complete Voice" />
<MenuItem value={5} primaryText="Complete Text" />
<MenuItem value={6} primaryText="Active Voice" />
<MenuItem value={7} primaryText="Active Text" />
</DropDownMenu>
</ToolbarGroup>
<ToolbarSeparator />
<ToolbarGroup>
<ToggleButton onChange={this.props.glueToggle}/>
</ToolbarGroup>
</Toolbar>
</div>
);
}
}
export default BaseBar;
and including the form like below
<form onSubmit={handleSubmit}>
<div>
<Field
name="basebar"
component={BaseBar}
label="project"
/>
</div>
<div>
<Field
name="subject"
component={renderTextField}
label="subject"
/>
</div>
</form>
But on submit, I am getting the values for subject field but not the basebar values, any suggestions or approach is greatly appreciated.
The error is that you're trying to use the Field outside the context of a connected Redux Form.
Perhaps you could create your shareable component without the <Field> component itself, and then use that component in as many forms as you need, wrapping it with Field. See example usage in the documentation:
http://redux-form.com/6.8.0/docs/api/Field.md/#usage
import MyCustomInput from './MyCustomInput'
...
<Field name="myField" component={MyCustomInput}/>
Where MyCustomInput is your custom, common component.
Or if you have custom logic to map Field props to your components, then use a function which you can then export and reuse in as many forms as you need.
// this is your custom component now
export const renderMyCustomField = (field) => (
<div className="input-row">
<input {...field.input} type="text"/>
{field.meta.touched && field.meta.error &&
<span className="error">{field.meta.error}</span>}
</div>
)
...
//which you can then import in your forms
import { renderMyCustomField } from '...'
// inside your render() method
<Field name="myField" component={renderMyCustomField}/>
Welcome to reusable redux form field component :)
Here my elegant soloution:
import React from 'react';
import { string, object } from 'prop-types';
import { Field } from 'redux-form/immutable';
const Input = ({
input,
meta: { touched, error },
...rest
}) => (
<div>
<input
{...input}
{...rest}
/>
{touched && error && <span>{error}</span>}
</div>
);
Input.propTypes = {
input: object.isRequired,
meta: object.isRequired,
type: string.isRequired
};
Input.defaultProps = {
input: null,
meta: null,
type: 'text'
};
export default props => <Field {...props} component={Input} />;
How to use?
import Input from './Input';
<form>
...
<Input
autoComplete="off"
name="email"
placeholder="Email"
type="email"
/>
...
</form>

how to provide validations for react js form

How to provide validations for react js form.
I have taken form with two fields. if two fields are enabled then only need to enable save button.
import React from 'react'
import { Button, Checkbox, Form } from 'semantic-ui-react'
const FormExampleForm = () => (
<Form>
<Form.Field>
<label>First Name</label>
<input placeholder='First Name' />
</Form.Field>
<Form.Field>
<label>Last Name</label>
<input placeholder='Last Name' />
</Form.Field>
<Form.Field>
<Checkbox label='I agree to the Terms and Conditions' />
</Form.Field>
<Button type='submit'>Submit</Button>
</Form>
)
export default FormExampleForm
In redux forms 6.7 there's a simple property called pristine which can be used to do this. But if the user enters some value in at least one input field the submit button gets activated. To achieve that you may need to do something like this.
render() {
// const { handleSubmit } = this.props;
const {handleSubmit, pristine, reset, submitting} = this.props
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<div>
<label>First Name</label>
<div>
<Field name="firstName" component="input" type="text" placeholder="First Name"/>
</div>
</div>
<button type="submit" className="btn btn-primary" disabled={pristine || submitting}>Submit</button>
</form>
);
}
}
But if you need to enable submit button, say when the user inputs values in 2 given fields or so, then this becomes complex. You need to do something like this. Here's my sample usecase. The form has 3 fields firstName, lastName and age. The submit button gets activated only if the user enters values for firstName and lastName input fields here. Please check out the following code.
import React, { Component } from 'react';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { connect } from 'react-redux';
class UserNew extends Component {
constructor(props) {
super(props);
this.isSubmitEnabled = this.isSubmitEnabled.bind(this);
}
onSubmit(values) {
console.log(values);
}
isSubmitEnabled() {
// Access field values here and validate them
const firstName = this.props.firstNameValue;
const lastName = this.props.lastNameValue;
if(firstName && lastName){
return true;
}
return false;
}
render() {
const { handleSubmit } = this.props;
const isEnabled = this.isSubmitEnabled();
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<div>
<label>First Name</label>
<div>
<Field name="firstName" component="input" type="text" />
</div>
</div>
<div>
<label>Last Name</label>
<div>
<Field name="lastName" component="input" type="text" />
</div>
</div>
<div>
<label>Age</label>
<div>
<Field name="age" component="input" type="text" />
</div>
</div>
<button type="submit" className="btn btn-primary" disabled={!isEnabled}>Submit</button>
</form>
);
}
}
UserNew = reduxForm({
form: 'UserNewForm'
})(
UserNew
);
// Decorate with connect to read form values
const selector = formValueSelector('UserNewForm') // <-- same as form name
UserNew = connect(state => {
const firstNameValue = selector(state, 'firstName')
const lastNameValue = selector(state, 'lastName')
return {
firstNameValue,
lastNameValue,
}
})(UserNew)
export default UserNew;
To access field values you need to use formValueSelector in ReduxForms. For that you need to connect your form to redux store using connect helper.
Hope this helps. Happy coding !
Either you can use Redux-form as its not used in above code, so you can write custom JS validation code. For this you need to follow below
Initialise state in
contructor(){
this.state{
first_name: null,
last_name: null,
}
}
Change state of every input element onChange onChange={()=>this.setState({first_name: event.target.value})}
Now add onClick property to <Button onClick={this.handleSubmit} />
inside handleSubmit() you can get values through local state of component and add validation rules like
if(this.state.first_name =="") {
console.log("Enter first name")
}

Resources