redux-form Dinamically inject objects into FieldArray `field` property - reactjs

What I am trying to accomplish is a dynamic FieldArray similar to the one in the redux form example but the difference is that every object I am adding to the list already has all properties (which i have to display) but one that I need to calculate from other two of them.
Take a look at this example:
import React from 'react'
import { compose } from 'redux';
import { connect } from 'react-redux';
import { Field, FieldArray, reduxForm, formValueSelector } from 'redux-form'
const renderField = ({ input, label, type, meta: { touched, error } }) => (
<div>
<label>{label}</label>
<div>
<input {...input} type={type} placeholder={label}/>
{touched && error && <span>{error}</span>}
</div>
</div>
)
const renderSelect = (products) => (
<div>
<label>Select Product to Add</label>
<div>
<Field name="products" component="select">
<option></option>
{products.map(p => <option value={p.id}>{p.name}</option>)}
</Field>
</div>
</div>
);
const renderCompetences = ({ fields, meta: { touched, error, submitFailed }}) => {
return (
<div>
{/* <div>
<button type="button" onClick={() => fields.push({})}>Add Competence</button>
{(touched || submitFailed) && error && <span>{error}</span>}
</div> */}
<ul>
{fields.map((competence, index) =>
<li key={index}>
<h4>Competence #{index + 1}</h4>
<Field
name={`${competence}.singlePrice`}
type="number"
component={renderField}
label="Single Price"/>
<Field
name={`${competence}.standardQuantity`}
type="number"
component={renderField}
label="Standard Quantity"/>
<Field
name={`${competence}.totalPrice`}
type="number"
component={renderField}
label="Total Price"/>
<button
type="button"
onClick={() => fields.remove(index)}
style={{color: 'red'}}
>
✘
</button>
</li>
)}
</ul>
</div>
);
}
const FieldArraysForm = (props) => {
const { handleSubmit, pristine, reset, submitting, products, productsValue } = props;
return (
<form onSubmit={handleSubmit}>
<Field name="recipientName" type="text" component={renderField} label="Recipient Name"/>
<FieldArray name="competences" component={renderCompetences} />
{renderSelect(products)}
<div>
<button type="submit" disabled={submitting}>Submit</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
</div>
</form>
)
}
const selector = formValueSelector('fieldArrays');
const mapStateToProps = (state) => {
const productsValue = selector(state, 'products');
return { productsValue };
};
export default compose(
connect(mapStateToProps),
reduxForm({
form: 'fieldArrays'
})
)(FieldArraysForm);
I should display the products I selected form the select in my unordered list and every time I select a new object I should add it to the list.
Here you can see an example of the products array:
const products = [
{ id: '0', name: 'Product One', singlePrice: 101, standardQuantity: 1},
{ id: '1', name: 'Product Two', singlePrice: 39, standardQuantity: 6},
{ id: '2', name: 'Product Three', singlePrice: 85, standardQuantity: 4},
{ id: '3', name: 'Product Four', singlePrice: 1, standardQuantity: 20}
];
Here is a working example to test some solutions:
https://www.webpackbin.com/bins/-Kecgj77Gbxo_4am3WS1
Thank you so much for any help you can give!

This is the solution i came up with:
import React from 'react'
import { compose } from 'redux';
import { connect } from 'react-redux';
import { Field, FieldArray, reduxForm, formValueSelector } from 'redux-form'
const renderField = ({ input, label, type, meta: { touched, error } }) => (
<div>
<label>{label}</label>
<div>
<input {...input} type={type} placeholder={label}/>
{touched && error && <span>{error}</span>}
</div>
</div>
)
const renderCalcField = ({ input, label, type, meta: { touched, error }, calc }) => (
<div>
<label>{label}</label>
<div>
<input {...input} type={type} placeholder={label} value={calc()} />
{touched && error && <span>{error}</span>}
</div>
</div>
)
const renderCompetences = ({ fields, meta: { touched, error, submitFailed }, products, productSelectValue }) => {
return (
<div>
<ul>
{fields.map((competence, index) =>
<li key={index}>
<h4>Competence #{index + 1}</h4>
<Field
name={`${competence}.singlePrice`}
type="number"
component={renderField}
label="Single Price"
onChange={() => {
const current = fields.get(index);
current.totalPrice = current.singlePrice * current.standardQuantity;
fields.remove(index);
fields.insert(index, current);
}}
/>
<Field
name={`${competence}.standardQuantity`}
type="number"
component={renderField}
label="Standard Quantity"
/>
<Field
name={`${competence}.totalPrice`}
type="number"
component={renderCalcField}
props={{calc: () => {
const current = fields.get(index);
return current.singlePrice * current.standardQuantity;
}}}
label="Total Price"
/>
<button
type="button"
onClick={() => fields.remove(index)}
style={{color: 'red'}}
>
✘
</button>
</li>
)}
</ul>
<div>
<Field name="productSelect" component="select">
<option>Select product</option>
{products.map(p => <option value={p.id}>{p.name}</option>)}
</Field>
<button type="button" onClick={() => {
const selectedProduct = products.find(p => p.id === productSelectValue);
fields.push(selectedProduct);
}}>Add</button>
{(touched || submitFailed) && error && <span>{error}</span>}
</div>
</div>
);
}
const FieldArraysForm = (props) => {
const { handleSubmit, pristine, reset, submitting, products, productSelectValue } = props;
return (
<form onSubmit={handleSubmit}>
<Field name="recipientName" type="text" component={renderField} label="Recipient Name"/>
<FieldArray name="competences" component={renderCompetences} props={{products, productSelectValue}}/>
<div>
<button type="submit" disabled={submitting}>Submit</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
</div>
</form>
)
}
const selector = formValueSelector('fieldArrays');
const mapStateToProps = (state) => {
const productSelectValue = selector(state, 'productSelect');
return { productSelectValue };
};
export default compose(
connect(mapStateToProps),
reduxForm({
form: 'fieldArrays'
})
)(FieldArraysForm);
working here on webpackbin (sometimes it has loading problems but it does not depend on my example)

Related

How to change a field in a redux-form?

In my react component I am trying to set a field called 'total'. I have imported the change action as a prop into my component:
import React, { Component, Fragment } from 'react'
import { Field, FieldArray, reduxForm, getFormValues, change } from 'redux-form'
import { connect } from 'react-redux'
import { CalcTotal } from './calculationHelper';
const renderField = ({ input, label, type, meta: { touched, error } }) => (
<div>
<label>{label}</label>
<div>
<input {...input} type={type} placeholder={label} />
{touched && error && <span>{error}</span>}
</div>
</div>
)
const renderMods = ({ fields, meta: { error, submitFailed } }) => (
<Fragment>
<ul>
<li>
<button type="button" onClick={() => fields.push({})}>
Add Modification
</button>
{submitFailed && error && <span>{error}</span>}
</li>
{fields.map((mod, index) => (
<li key={index}>
<button
type="button"
title="Remove Mod"
onClick={() => fields.remove(index)}
/>
<h4>Mod #{index + 1}</h4>
<Field
name={`${mod}.lastYear`}
type="number"
component={renderField}
label="Last Year"
/>
<Field
name={`${mod}.currentYear`}
type="number"
component={renderField}
label="Current Year"
/>
<Field name={`${mod}.type`} component="select" label="Type">
<option />
<option value="-">Expense</option>
<option value="+">Income</option>
<option value="-">Tax</option>
</Field>
</li>
))}
</ul>
<Field
name="total"
type="number"
component="input"
label="Total modifications"
text="0"
/>
</Fragment>
)
class FieldArraysForm extends Component {
render() {
const { handleSubmit, formValues, change } = this.props
if (formValues) {
console.log('formvalues', formValues);
const test = CalcTotal(2000);
console.log('calc=', test);
debugger
this.props.change('fieldArraysForm', 'total', 5000)
}
return (
<form onSubmit={handleSubmit}>
{/* <button onClick={this.changeStuff}>set total</button> */}
<FieldArray name="mods" component={renderMods} />
<div>
<button type="submit" >
Submit
</button>
</div>
</form>
)
}
}
const mapStateToProps = (state) => ({
formValues: getFormValues('fieldArraysForm')(state),
});
const mapDispatchToProps = {
change
};
// const Example = reduxForm({
// form: 'fieldArraysForm', // a unique identifier for this form
// })(FieldArraysForm)
// const ConnectedForm = connect(
// mapStateToProps,
// mapDispatchToProps,
// )(Example);
// export default ConnectedForm
export default reduxForm({
form: "fieldArraysForm"
})(
connect(
mapStateToProps,
mapDispatchToProps
)(FieldArraysForm)
);
The line where the code fall into an infinite loop:
this.props.change('fieldArraysForm', 'total', 5000)
How /where do I put this statement to make sure the 'total' field is changed and not get into a loop?Which React lifecycle event would suit? I want to fire this whenever there is a form change on any field.
You'll need to move your statement out of the render method and into the componentDidUpdate lifecycle method (you also need an if statement to prevent an infinite loop):
componentDidUpdate(prevProps) {
if (this.props.someValue !== prevProps.someValue) {
this.props.change("formName", "formField", "newFormValue");
}
}
Working example: https://codesandbox.io/s/r5zz36lqnn (selecting the Has Email? radio button populates the email field with test#example.com, unselecting the radio button resets the email field to "")

redux form dynamic categories

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

FieldArray re-registering fields after each char-input

Hi I'm quite new to React and I'm trying to learn how to work with redux-forms (https://redux-form.com/7.3.0/) and it mostly works, but I am encountering an issue which I cant solve. The issue is the same as this one: https://github.com/erikras/redux-form/issues/2412
The issue in short:
Whenever i try to write something in an input-field, my form constantly re-registers the field, which makes the form almost impossible to work with. The bad behavior happens because of validation of inputs.
Although Marnusw seems to have solved it with this comment:
<Field ... validate={[required, minLength(6)]}/>
Every time the form was rendered a new minLength instance would be
created which caused the field to UNREGISTER_FIELD and then
REGISTER_FIELD again.
The minLength instance should be created once outside of the render
function:
const minLength6 = minLength(6)
<Field ... validate={[required, minLength6]}/>
However I am not connecting my validation to the component like he is, so I am unsure of how I can declare my validation outside the render() function in my code.
I have tried doing:
...(imports)
const validation = validate //declaring a const and assigning 'validate' to it
//instead of directly using validate.
MenuInputFields = reduxForm({
form: 'inputItemList',
validation
})(MenuInputFields)
But this disabled validation alltogether, which (of course) also solves the problem, but this is not a solution, since I need to validate inputs.
My component:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { reduxForm, Field, FieldArray } from 'redux-form'
import { menuSelector } from '../reducers/menuViewReducer'
import { postMenuRequest } from '../sagas/adminMenuSaga'
import validate from './validateForm'
const mapDispatchToProps = (dispatch) => {
return {
submitForm: (newMenu) => dispatch(postMenuRequest(newMenu))
}
}
export class MenuInputFields extends React.Component {
constructor(props){
super(props)
}
render() {
let currentDate = new Date()
let formattedDate = currentDate.toLocaleDateString('en-GB')
const renderField = ({ input, label, type, className, meta: { touched, error } }) => (
<div>
<input {...input} type={type} placeholder={label} className={className} />
{touched && error && <span>{error}</span>}
</div>
);
const renderMenuItem = ({fields, meta: {touched, error, submitFailed }}) => (
<ul className='adminInputFoodItem'>
{fields.map((MenuItem, index) => (
<li key={index}>
<p className='col-md-1'> {index+1} - </p>
<Field
name={`${MenuItem}.FoodItem`}
type='text'
component={renderField}
label='Titel'
className='col-md-2'
/>
<Field
name={`${MenuItem}.Description`}
type='text'
component={renderField}
label='Beskrivelse'
className='col-md-6'
/>
<Field
name={`${MenuItem}.Price`}
type='number'
component={renderField}
label='Pris'
className='col-md-1'
/>
<button
type='button'
title='Fjern'
onClick={() => fields.remove(index)}
className='btn btn-default btn-xs remItemRow'
>Fjern
</button>
</li>
))}
<li>
</li>
<li>
<button
type='button'
onClick={() => fields.push({})}
className='btn btn-default btn-sm addItemRow'
>Tilføj
</button>
{submitFailed && error && <span>{error}</span>}
</li>
</ul>
);
const { handleSubmit, pristine, reset, submitting, submitForm } = this.props
return (
<form className='adminInputForm' onSubmit={handleSubmit(submitForm)}>
<label className='col-md-2' >Dato: {formattedDate}</label>
<br />
<br />
<ul className='adminInputFoodItem'>
<li>
<label className='col-md-1'> Lukketid: </label>
<Field
name='ClosingTime'
type='text'
component={renderField}
label='TT:mm'
className='col-md-1'
/>
</li>
</ul>
<hr />
<div>
<label className='col-md-1'> # </label>
<label className='col-md-2'> Titel </label>
<label className='col-md-3'> Beskrivelse </label>
<label className='col-md-1'> Pris </label>
</div>
<br />
<br />
<FieldArray name='MenuItems' component={renderMenuItem} />
<br />
<div className=''>
<button
className='btn btn-default'
type='submit'
disabled={pristine || submitting}
>Gem Menu
</button>
<label>Submit besked her</label>
</div>
</form>
)
}
}
MenuInputFields = reduxForm({
form: 'inputItemList',
validate,
})(MenuInputFields)
export default connect(mapStateToProps, mapDispatchToProps)(MenuInputFields)
validation.js
//regex patterns to match against
const timeFormat = /^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/
const specialCharsTextInput = /^[^|<>\"&]*$/
const emptyTextInput = /^$/
//Error texts
const requiredErrText = 'Required'
const timeErrText = ' - Forkert input - Format: TT:MM'
const noItemsErrText = { _error: 'Tilføj mindst 1 menu' }
const validate = values => {
const errors = {}
if (!values.ClosingTime || emptyTextInput.test(values.ClosingTime)) {
errors.ClosingTime = requiredErrText
}
if(values.ClosingTime && !timeFormat.test(values.ClosingTime)){
errors.ClosingTime = timeErrText
}
if (!values.MenuItems || !values.MenuItems.length) {
errors.MenuItems = noItemsErrText
} else {
const MenuItemsArrayErrors = []
values.MenuItems.forEach((MenuItem, MenuItemIndex) => {
const MenuItemErrors = {}
if (!MenuItem || !MenuItem.FoodItem || !specialCharsTextInput.test(MenuItem.FoodItem) || !emptyTextInput.test(MenuItem.FoodItem)) {
MenuItemErrors.FoodItem = requiredErrText
MenuItemsArrayErrors[MenuItemIndex] = MenuItemErrors
} else if (!MenuItem || !MenuItem.Description || !specialCharsTextInput.test(MenuItem.Description) || !emptyTextInput.test(MenuItem.Description)) {
MenuItemErrors.Description = requiredErrText
MenuItemsArrayErrors[MenuItemIndex] = MenuItemErrors
} else if (!MenuItem || !MenuItem.Price || !specialCharsTextInput.test(MenuItem.Price) || !emptyTextInput.test(MenuItem.Price)) {
MenuItemErrors.Price = requiredErrText
MenuItemsArrayErrors[MenuItemIndex] = MenuItemErrors
}
})
if (MenuItemsArrayErrors.length) {
errors.MenuItems = MenuItemsArrayErrors
}
}
return errors
}
export default validate

unable to set defaultValue in redux-form-material-ui TextField

I'm unable to set a default value for the textField component.
I tried using default value, but as long as I'm using the redux-form-material-ui it just doesn't work.
I really don't understand what am I doing wrong (seems pretty basic)...
exmaple (just changed their fieldArray example a little):
import React from 'react'
import { Field, FieldArray, reduxForm } from 'redux-form'
import validate from './validate'
import {TextField} from 'redux-form-material-ui'
const renderField = (props) => {
console.log(props);
const { input, label, type, meta: { touched, error } } = props;
console.log(input);
return <div>
<label>{label}</label>
<div>
// Will not show "someValue", it will just be blank
<TextField defaultValue="someValue" {...input} type={type} placeholder={label}/>
{touched && error && <span>{error}</span>}
</div>
</div>
}
const renderMembers = ({ fields, meta: { touched, error, submitFailed } }) => (
<ul>
<li>
<button type="button" onClick={() => fields.push({})}>Add Member</button>
{(touched || submitFailed) && error && <span>{error}</span>}
</li>
{fields.map((member, index) =>
<li key={index}>
<button
type="button"
title="Remove Member"
onClick={() => fields.remove(index)}/>
<h4>Member #{index + 1}</h4>
<Field
name={`${member}.firstName`}
type="text"
component={renderField}
label="First Name"/>
<Field
name={`${member}.lastName`}
type="text"
component={renderField}
label="Last Name"/>
<FieldArray name={`${member}.hobbies`} component={renderHobbies}/>
</li>
)}
</ul>
)
const renderHobbies = ({ fields, meta: { error } }) => (
<ul>
<li>
<button type="button" onClick={() => fields.push()}>Add Hobby</button>
</li>
{fields.map((hobby, index) =>
<li key={index}>
<button
type="button"
title="Remove Hobby"
onClick={() => fields.remove(index)}/>
<Field
name={hobby}
type="text"
component={renderField}
label={`Hobby #${index + 1}`}/>
</li>
)}
{error && <li className="error">{error}</li>}
</ul>
)
const FieldArraysForm = (props) => {
const { handleSubmit, pristine, reset, submitting } = props
return (
<form onSubmit={handleSubmit}>
<Field name="clubName" type="text" component={renderField} label="Club Name"/>
<FieldArray name="members" component={renderMembers}/>
<div>
<button type="submit" disabled={submitting}>Submit</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>
</div>
</form>
)
}
export default reduxForm({
form: 'fieldArrays', // a unique identifier for this form
validate
})(FieldArraysForm)
Thanks.
I was having the same issue. Just read through the documentation again and saw this
No Default Values
Because of the strict "controlled component" nature of redux-form, some of the Material UI functionality related to defaulting of values has been disabled e.g. defaultValue, defaultDate, defaultTime, defaultToggled, defaultChecked, etc. If you need a field to be initialized to a certain state, you should use the initialValues API of redux-form.
It's probably only one way to make form of editing.
It cannot be restored from initial redux Form values.
Input
export default function ({ input, label, meta: { touched, error }, ...custom }) {
if ( input.value === '' && custom.cvalue ) { // hack for redux form with material components
input.onChange(String(custom.cvalue));
}
return (
<TextField
{...input}
{...custom}
fullWidth={true}
hintText={label}
floatingLabelText={label}
errorText={touched && error}
/>
)
}
Select
export default function ({ input, label, meta: { touched, error }, onChange, children, ...custom }) {
if ( input.value === '' && custom.cvalue ) { // hack for redux form with material components
if ( is.function(onChange) ) {
onChange(custom.cvalue);
}
input.onChange(custom.cvalue);
}
return (
<SelectField
{...input}
{...custom}
fullWidth={true}
children={children}
floatingLabelText={label}
errorText={touched && error}
onChange={(event, index, value) => {
if ( is.function(onChange) ) { // and custom onChange for f....g ....
value = onChange(value);
}
input.onChange(value);
}}/>
)
}
then in template
its can be way to make form of editing existing entity ....
<Field
name="study"
label="Studies"
component={ FormSelect }
cvalue={this.state.study.id}
onChange={value => {
this.setState({study: _.find(studies, {id: value})||{id: 0}});
return value;
}}>
{studies.map( (study, key) => ( <MenuItem key={key} value={study.id} primaryText={study.officialTitle} /> ))}
</Field>

Redux form with self creating inputs

I'm trying to create a redux form (using redux-form) that can dynamically create it's own inputs. I am having trouble figuring out how to make redux-form aware of the new fields that have been created. Is it possible to dynamically change the fields prop that redux-form passes in within the form component itself? Am I thinking about this wrong? Here is what I am working with.
class AddCustomer extends Component {
render() {
class Form extends Component {
constructor(props, context) {
super(props, context)
this.state = {
inputsToAdd: []
};
}
handleAddInput() {
let inputsToAdd = this.state.inputsToAdd.slice();
inputsToAdd.push(this.state.inputsToAdd.length);
this.setState({ inputsToAdd });
}
submitForm(data) {
console.log(data)
this.setState({inputsToAdd: []});
this.props.dispatch(initialize('addCustomer', {}))
}
render() {
const { fields, handleSubmit } = this.props;
return (
<div>
<form onSubmit={handleSubmit(this.submitForm.bind(this))}>
<Input type='text' label='Company name' {...fields['companyName']}/>
<Input type='email' label='Admin user email' {...fields['adminEmail']}/>
</form>
{this.state.inputsToAdd.map((element, index) => {
return (
<Input key={index} type='text' />
)
})}
<Button onClick={() => this.handleAddInput()}>Add Input</Button>
<Button onClick={handleSubmit(this.submitForm.bind(this))}>Submit</Button>
</div>
)
}
}
Form = connectReduxForm({
form: 'addCustomer',
fields: ['companyName', 'adminEmail']
})(Form);
return (
<div>
<h1>Add Customer</h1>
<Form />
</div>
)
}
}
As of Redux Form 6.* you can achieve what you are trying to do using <FieldArray/>
See the example below (taken from Redux documentation and slightly simplified)
import React from 'react'
import { Field, FieldArray, reduxForm } from 'redux-form'
import validate from './validate'
const renderMembers = ({ fields, meta: { touched, error } }) => (
<ul>
<li>
<button type="button" onClick={() => fields.push({})}>Add Member</button>
{touched && error && <span>{error}</span>}
</li>
{fields.map((member, index) =>
<li key={index}>
<button
type="button"
title="Remove Member"
onClick={() => fields.remove(index)}/>
<h4>Member #{index + 1}</h4>
<Field
name={`${member}.firstName`}
type="text"
component={renderField}
label="First Name"/>
<Field
name={`${member}.lastName`}
type="text"
component={renderField}
label="Last Name"/>
</li>
)}
</ul>
)
const FieldArraysForm = (props) => {
const { handleSubmit, submitting } = props
return (
<form onSubmit={handleSubmit}>
<FieldArray name="members" component={renderMembers}/>
<div>
<button type="submit" disabled={submitting}>Submit</button>
</div>
</form>
)
}
export default reduxForm({
form: 'fieldArrays', // a unique identifier for this form
validate
})(FieldArraysForm)
For more info checkout the documentation
http://redux-form.com/6.1.1/examples/fieldArrays/

Resources