React Form Package
Edit page
IntroductionInstallationFormSimple FormButtonFieldFieldWrapperRadioGroupSelectForm ValidationStateonFocus onChange onBlurState ManipulationCustom Error MessagesFeedback on disabled ButtonStylingDynamic FieldsDynamic Fields 2onClickonChangeDynamic Fields 3Bind Input FieldsBind Input Fields 2Third Party ComponentsFile UploadWhy

Dynamic Fields

Sometimes you do not know how many fields your form should have, so you need a way to add dynamic fields on user events, e.g. on a button click if a user should decide if a new field is needed, or on a change of a field, e.g. when a field is filled a new field should appear.

onClick

First off, import your components.

import React from 'react';
import {
Form,
Field,
Button,
} from 'react-form-package';

The next step is to create a class that will render our form.

The state is allways handled by react-form-package, the only thing that you need todo is to handle the appearence of the form, e.g. add or remove the input.

We need create a addField, removeField, and optionally a calculateAvailableId function. The calculateAvailableId is only nessacary if you work on non unique index-based ids so that you ensure you do not overide an existing id.

The addField function is here to add a new Field when you click on a <Button /> component.

To update the state of the <Form /> component, you need to add a rfpRole property and a field or a fieldId property to a <Button /> component.

The rfpRole takes a string which is either addField or removeField.

If you use the <Button /> component to add a new field to the state of the form component you need to provide a field property which takes an object that represents your new <Field /> component. This object has to have at least a id and a type, but you can extend this object with rules like: min, max, required, match, and sameAs.

If you use the <Button /> component to add remove an existing field from the state of the form component you need to provide a fieldId property which takes a string: the id of the field you want to remove.

In the state of the DynamicFields component you have to create an array where you add refrences to the fields of your dynamic <Field /> components. To add new fields you need to add a new refrence, so that the part of the DOM rerenders with the new Field Component. To remove the Fields not only from the state of the <Form /> component but also from the DOM, you need to remove the refrences in your state that the component rerenders that part of the DOM.

class DynamicFields extends React.Component {
constructor(props) {
super(props);
this.state = {
// the field refrences that we use to render in our <Form /> component
companies: [
{
id: 'company-0',
},
],
};
this.addField = this.addField.bind(this);
this.removeField = this.removeField.bind(this);
this.calculateAvailableId = this.calculateAvailableId.bind(this);
}
calculateAvailableId() {
const {
companies,
} = this.state;
const arr = companies.map((item) => parseInt(item.id.split('-')[1], 10));
let currentHighestId = Math.max(...arr);
currentHighestId = currentHighestId !== -Infinity ? currentHighestId : 0;
const highestAvailableId = currentHighestId + 1;
return highestAvailableId;
}
addField() {
const {
companies,
} = this.state;
const highestAvailableId = this.calculateAvailableId();
// add a new field refrence to the <Form /> component
this.setState({
companies: companies.concat({ id: `company-${highestAvailableId}` }),
});
}
removeField(idx) {
const {
companies,
} = this.state;
// remove a field refrence to the <Form /> component
this.setState({
companies: companies.filter((_, index) => idx !== index),
});
}
render() {
const {
companies,
} = this.state;
const highestAvailableId = this.calculateAvailableId();
return (
<Form
validate
>
{/* render the <Field /> components based on our field refrences */}
{companies.map((company, idx) => (
<div>
<Field
id={company.id}
placeholder={`Company ${company.id.split('-')[1]}`}
type="text"
required
/>
<Button
id="removeField"
// add the rfpRole property
rfpRole="removeField"
type="button"
// add the fieldId property to remove the field from the state of the <Form /> component
fieldId={company.id}
onClick={() => this.removeField(idx)}
>
Remove Company
</Button>
</div>
))}
<div>
<Button
id="addField"
// add the rfpRole property
rfpRole="addField"
type="button"
// add the field property to add to the state state of the <Form /> component
field={{
id: `company-${highestAvailableId}`,
type: 'text',
required: true,
}}
onClick={() => this.addField()}
>
Add Company
</Button>
</div>
<div>
<Button
id="submit"
type="submit"
onClick={(state) => {
alert(JSON.stringify(state, null, 2));
alert('open the console to see the whole state...');
console.log(state);
}}
>
submit
</Button>
</div>
</Form>
);
}
}
export { DynamicFields };

Now render this Component. Everytime you click on the button Add Company you get a new field, and everytime you click on the button Remove Company you remove the corresponding field.

onChange

First off, import your components.

import React from 'react';
import {
Form,
Field,
Button,
} from 'react-form-package';

The next step is to create a class that will render our form.

Everything stays the same as in the example above, except:

That we now use a dynamic property and the field property on the <Field /> component. the field property takes the same properties as in the <Button /> component. The dynamic property indicates that this <Field /> component is dynamic and adds a new field in the state of the <Form /> component when this <Field /> is filled.

class DynamicFields extends React.Component {
constructor(props) {
super(props);
// the field refrences that we use to render in our <Form /> component
this.state = {
companies: [
{
id: 'company-0',
},
],
};
this.addField = this.addField.bind(this);
this.removeField = this.removeField.bind(this);
this.calculateAvailableId = this.calculateAvailableId.bind(this);
}
calculateAvailableId() {
const {
companies,
} = this.state;
const arr = companies.map((item) => parseInt(item.id.split('-')[1], 10));
let currentHighestId = Math.max(...arr);
currentHighestId = currentHighestId !== -Infinity ? currentHighestId : 0;
const highestAvailableId = currentHighestId + 1;
return highestAvailableId;
}
addField(state, id) {
const {
companies,
} = this.state;
const highestAvailableId = this.calculateAvailableId();
// add a new field refrence to the <Form /> component
if (state.data[id] && parseInt(id.split('-')[1], 10) + 1 === highestAvailableId) {
this.setState({
companies: companies.concat({ id: `company-${highestAvailableId}` }),
});
}
}
removeField(idx) {
const {
companies,
} = this.state;
// remove a field refrence to the <Form /> component
this.setState({
companies: companies.filter((_, index) => idx !== index),
});
}
render() {
const {
companies,
} = this.state;
const highestAvailableId = this.calculateAvailableId();
return (
<Form>
{/* render the <Field /> components based on our field refrences */}
{companies.map((company, idx) => (
<div>
<Field
id={company.id}
placeholder={`Company ${company.id.split('-')[1]}`}
type="text"
required
// add the dynamic property
dynamic
// add the field property to add to the state state of the <Form /> component
field={{
id: `company-${highestAvailableId}`,
type: 'text',
required: true,
}}
onChange={(state) => this.addField(state, company.id)}
/>
{companies.length && (
<Button
id="removeField"
// add the rfpRole property
rfpRole="removeField"
type="button"
// add the fieldId property to remove the field from the state of the <Form /> component
fieldId={company.id}
onClick={() => this.removeField(idx)}
>
Remove Company
</Button>
)}
</div>
))}
<div>
<Button
id="submit"
type="submit"
onClick={(state) => {
alert(JSON.stringify(state, null, 2));
alert('open the console to see the whole state...');
console.log(state);
}}
>
submit
</Button>
</div>
</Form>
);
}
}
export { DynamicFieldsOnChange };

Now render this Component. Everytime you fill out the <Field /> component you will add a new <Field />, and everytime you click on the button Remove Company you remove the corresponding field.