Dynamic Forms in Netlify - reactjs

I have two components in React that in a sense are both forms. One component is a parent form of some sorts that contains a map function that iterates over a children state item to produce a number of children sub forms. This means that people using the forms can click a button to add a new instance of a ChildSubForm component beneath the button in the parent form.
My current issue is that I cannot get the form to pick up the children in Netlify forms. I have data-netlify="true" and the hidden input so the parent form is recognised, however when I submit the form, only the inputs in the parent form are picked up, how can I change my settings or code to allow netlify to detect the dynamic components' values so that they are sent alongside the other data?
As you can see, I also attempted to store all form data in state items and submit them, but I cannot see the values still. Is it something to do with Netlify building the static site and detecting all form values beforehand?
BookNow.js (The parent form)
import React, { Component } from 'react'
import ChildSubForm from './ChildSubForm'
export default class BookNow extends Component {
state = {
name: "",
email: "",
otherInfo: "",
children: []
}
constructor(props) {
super(props)
this.addChild = this.addChild.bind(this);
this.decrementChild = this.decrementChild.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
addChild() {
this.setState({
children: [...this.state.children, { name: "", year: "Year 7" }]
})
}
decrementChild(i) {
this.setState({
children: this.state.children.filter((item, j) => i !== j)
})
}
handleChange(e) {
if (["name", "year"].includes(e.target.className)) {
let children = [...this.state.children]
children[e.target.dataset.id][e.target.className] = e.target.value
this.setState({ children }, () => console.log(this.state.children))
} else {
this.setState({ [e.target.name]: e.target.value })
}
}
handleSubmit(e) {
e.preventDefault()
}
render() {
if(this.props.isChild) {
return (
<form name="Booking Form" method="POST" data-netlify="true">
<input type="hidden" name="form-name" value="Booking Form" />
<input type="text" name="name" placeholder="Your Name" />
<select name="year-group">
<option value="Year 7">Year 7</option>
<option value="Year 8">Year 8</option>
<option value="Year 9">Year 9</option>
<option value="Year 10">Year 10</option>
<option value="Year 11">Year 11</option>
<option value="Year 12">Year 12</option>
<option value="Year 13">Year 13</option>
<option value="GCSE Retake">GCSE Retake</option>
</select><br />
<input type="email" name="email" placeholder="Your Email Address" /><br />
<textarea name="otherInfo" placeholder="Any more information..." /><br />
<button type="submit">Book Now</button>
</form>
)
} else {
return (
<form onChange={this.handleChange} onSubmit={this.handleSubmit} name="Booking Form" method="POST" data-netlify="true">
<input type="hidden" name="form-name" value="Booking Form" />
<input type="text" name="name" placeholder="Your Name" /><br />
<input type="email" name="email" placeholder="Your Email Address" /><br />
<button onClick={this.addChild} name="add">+ Add Child</button><br />
{
this.state.children.map((child, i) => <ChildSubForm key={i} childNum={i} value={this.state.children[i]} dec={() => this.decrementChild(i)} />)
}
<textarea name="otherInfo" placeholder="Any more information..." /><br />
<button type="submit">Book Now</button>
</form>
)
}
}
}
ChildSubForm.js
import React, { Component } from 'react'
export default class ChildSubForm extends Component {
render() {
const values = {
name: `child-name-${this.props.childNum}`,
year: `child-${this.props.childNum}-year-group`
}
return (
<div className="ChildSubForm">
<input type="text" id={values.name} name={values.name} data-id={this.props.childNum} value={this.props.value.name} className="name" placeholder="Child's Name" required />
<select name={values.year} id={values.year} data-id={this.props.childNum} id={values.year} value={this.props.value.year} className="year">
<option value="Year 7">Year 7</option>
<option value="Year 8">Year 8</option>
<option value="Year 9">Year 9</option>
<option value="Year 10">Year 10</option>
<option value="Year 11">Year 11</option>
<option value="Year 12">Year 12</option>
<option value="Year 13">Year 13</option>
<option value="GCSE Retake">GCSE Retake</option>
</select>
<button onClick={this.props.dec} name="remove">- Remove Child</button><br />
</div>
)
}
}
UPDATE:
So I did some reading and found a post looking to do the same thing and the Director of Support for Netlify saying it isn't possible yet unless you predefine all of the form fields beforehand (for each of the mapped components). Has anybody found a workaround for this?
Netlify Post

Related

react-router-dom i can't printing the default value from the form to another page

I want to print the information I get from the form on the info page when I press the send button. However, when the information is taken as default value, it does not come to the info page.
Information from the form needs to be printed on the Info page
My App.js
import React, { useState } from 'react'
import './App.css';
import { Route, Routes } from 'react-router-dom'
import Info from './Info';
import Form from './Form';
function App() {
const [form, setForm] = useState({ name: "", city: "", birth: "", color: "", address: "" });
const handleChange = (event) => {
setForm({ ...form, [event.target.name]: event.target.value })
}
return (
<div className="App">
<h2>Enter your informations</h2>
<Routes>
<Route path="/" exact element={<Form form={form} onChange={handleChange}/>}></Route>
<Route path="/info" element={<Info form={form}/>}></Route>
</Routes>
</div>
);
}
export default App;
My Form.js
import React from 'react'
import { Link } from 'react-router-dom'
function Form({ form, HandleChange }) {
return (
<div>
<form>
<input type="text" name="isim" defaultValue={form.name} onChange={HandleChange} placeholder="Enter your name" />
<select name="city" defaultValue={form.city} onChange={HandleChange}>
<option defaultValue="">Enter your city</option>
<option defaultValue="Ankara">Ankara</option>
<option defaultValue="İstanbul">İstanbul</option>
<option defaultValue="İzmir">İzmir</option>
</select>
<input type="date" name="birth" defaultValue={form.birth} onChange={HandleChange} />
<input type="color" name="color" defaultValue={form.color} onChange={HandleChange} />
<textarea name="address" placeholder="Enter your address" cols="20" rows="5" defaultValue={form.adsress} onChange={HandleChange}></textarea>
<Link to="/info"><button>Send</button></Link>
</form>
</div>
)
}
export default Form
My Info.js
import React from 'react'
import { Link} from 'react-router-dom'
function Info({form}) {
return (
<div>
<p>Name: {form.name} </p>
<p>City: {form.city}</p>
<p>Birthday: {form.birth}</p>
<p>Color: {form.color}</p>
<p>Address: {form.address}</p>
<Link to="/"><button>Back</button></Link>
</div>
)
}
export default Info
First issue is that the Form component is passed an onChange prop but is destructuring a HandleChange prop.
<Route
path="/"
element={<Form form={form} HandleChange={handleChange} />}
/>
Fix this to pass a consistently named prop.
<Route
path="/"
element={<Form form={form} onChange={handleChange} />}
/>
The second issue from what I can see of the code it seems the button element is submitting the form and since the default form action is not prevented the page/app is reloading. HTML button elements have type="submit" by default if not specified.
Make sure that the name attribute for the "name" field matches the React state key. In other words, ensure it is name="name" instead of name="isim".
Additionally, all these form fields should technically be controlled inputs since you are attaching an onChange handler to each. Controlled inputs use the value prop instead of the defaultValue prop.
You've a couple options:
Declare the button to not be a form submit button.
function Form({ form, onChange }) {
return (
<div>
<form>
<input
type="text"
name="name"
value={form.name}
onChange={onChange}
placeholder="Enter your name"
/>
<select name="city" value={form.city} onChange={onChange}>
<option disabled value="">Enter your city</option>
<option value="Ankara">Ankara</option>
<option value="İstanbul">İstanbul</option>
<option value="İzmir">İzmir</option>
</select>
<input
type="date"
name="birth"
value={form.birth}
onChange={onChange}
/>
<input
type="color"
name="color"
value={form.color}
onChange={onChange}
/>
<textarea
name="address"
placeholder="Enter your address"
cols="20"
rows="5"
value={form.adsress}
onChange={onChange}
></textarea>
<Link to="/info">
<button type="button">Send</button>
</Link>
</form>
</div>
);
}
Declare a form submit handler and prevent the default form action and issue the imperative navigation action.
import { useNavigate } from 'react-router-dom';
function Form({ form, onChange }) {
const navigate = useNavigate();
const submitHandler = (e) => {
e.preventDefault();
navigate("/info");
};
return (
<div>
<form onSubmit={submitHandler}>
<input
type="text"
name="name"
value={form.name}
onChange={onChange}
placeholder="Enter your name"
/>
<select name="city" value={form.city} onChange={onChange}>
<option disabled value="">Enter your city</option>
<option value="Ankara">Ankara</option>
<option value="İstanbul">İstanbul</option>
<option value="İzmir">İzmir</option>
</select>
<input
type="date"
name="birth"
value={form.birth}
onChange={onChange}
/>
<input
type="color"
name="color"
value={form.color}
onChange={onChange}
/>
<textarea
name="address"
placeholder="Enter your address"
cols="20"
rows="5"
value={form.adsress}
onChange={onChange}
></textarea>
<button type="submit">Send</button>
</form>
</div>
);
}
In either case you'll still want to be explicit about the button type for readability and maintainability reasons.
Demo

how to hide and show input conditionally in React Js

I Know this approach is not good in react js. but i don't know how to achieve same with react js. not getting the right way of doing this.
I have multiple input which I have to hide and show based on value selected from dropdown.
I am making this demo code for anyone who want to check.
https://codesandbox.io/s/gracious-hertz-k2ttl8?file=/src/App.js:0-1084
App.js
const App = () => {
const getInput = (e) => {
let input = e.target.value;
let data = document.getElementsByClassName(input);
data.array.forEach((ele) => {
if (ele.style.display === "none") {
ele.style.display = "block";
}
});
};
return (
<>
<select onChange={getInput}>
<option value="Address">Address</option>
<option value="Name">Name</option>
<option value="Both">Name && Address</option>
</select>
<br />
<br />
<input
className="Address Both"
type="text"
name="city"
placeholder="City"
/>
<input
className="Address Both"
type="text"
name="pincode"
placeholder="pincode"
/>
<br />
<br />
<input
className="Name Both"
type="text"
name="firstName"
placeholder="First Name"
/>
<input
className="Name Both"
type="text"
name="firstName"
placeholder="Last Name"
/>
</>
);
};
export default App;
You can use a state to store the selected value and then render the input based on that state:
const App = () => {
const [selected, setSelected] = useState("");
const getInput = (e) => {
setSelected(e.target.value);
};
return (
<>
<select onChange={getInput}>
<option value="Address">Address</option>
<option value="Name">Name</option>
<option value="Both">Name && Address</option>
</select>
<br />
<br />
{selected === 'Address'&&<input
className="Address Both"
type="text"
name="city"
placeholder="City"
/>}
// Similarly for the other inputs
</>
);
};
You can take a state that keeps track of the selected dropDown items.
const [selectedItem, setSelectedItem] = useState({
city: false,
pincode: false,
...
})
Then based on the state show the input put conditionally in the JSX something like the following codes:
{
selectedItem.city &&
<input
className="Address Both"
type="text"
name="city"
placeholder="City"
/>
}
You can follow the same thing for other input values also.
and don't forget to update selectedItem state on dropDown onChange function
i have made changes in your codesandbox , you need to use conditional rendering to show or hide component.
thank everyone who did answer to help me out
finally it working just as I want and I figure it out it was extra "br/" tag" which was the problem in the code
final code :
App.js
import { useState } from "react";
const App = () => {
const [display, setdisplay] = useState({
city: true,
pincode: true,
firstName: true,
lastName: true
});
const getInput = (e) => {
let input = e.target.value;
switch (input) {
case "Address": setdisplay({city: false, pincode: false, firstName: true, lastName: true }); break;
case "Name": setdisplay({ city: true, pincode: true, firstName: false, lastName: false }); break;
case "Both": setdisplay({city: false, pincode: false, firstName: false, lastName: false }); break;
default: setdisplay({});
}
};
return (
<>
<select onChange={getInput}>
<option value="Address">Address</option>
<option value="Name">Name</option>
<option value="Both">Both</option>
</select>
<br />
<input
style={{ display: display.city ? "none" : "inline-block" }}
className="Address Both"
type="text"
name="city"
placeholder="City"
/>
<input
style={{ display: display.pincode ? "none" : "inline-block" }}
className="Address Both"
type="text"
name="pincode"
placeholder="pincode"
/>
<input
style={{ display: display.firstName ? "none" : "inline-block" }}
className="Name Both"
type="text"
name="firstName"
placeholder="First Name"
/>
<input
style={{ display: display.lastName ? "none" : "inline-block" }}
className="Name Both"
type="text"
name="firstName"
placeholder="Last Name"
/>
</>
);
};
export default App;

how to avoid repeating pattern when creating form in react

I am going to update the form with each keystroke with useState Hook this way I have to add an onChange event listener plus a function for each input and as you can imagine it's going to be lots of functions how can I avoid repeating myself?
function firstNamInput(){
}
function lastNameInput(){
}
function emailInput(){
}
function phoneInput(){
}
function addressInput(){
}
function genderInput(){
}
function jobInput(){
}
function ageInput(){
}
in react we try to collect form data on every keystroke but in the past, we used to collect form data when submit clicked. so because of this new approach, we have to listen for every keystroke on every input! isn't this crazy? yes, kind of but there is a way to go around I am going to explain it to you :
first I am going to talk about Rules you have to know about inputs:
we are going to use a useState Hook and we pass an object to it which contain:
a property for every input that's single
a property for group inputs (for example if there is checkboxes for gender we create only one property for gender)
what attributes every input should have?
name (it's going to be equal to its property in the useState)
value or checked (as a rule of thumb if the inputs gets true or false its usually checked vice versa )
onChange event
so let's create useState I am going to have a total of 7 properties in the object:
const [formData, setFormData] = React.useState(
{
firstName: "",
lastName: "",
email: "",
comments: "",
isFriendly: true,
employment: "",
favColor: ""
}
)
we need to create a function for the onChange event i am going to make it as reusable as possible it's going to get form data from each input and update it in our Hook.
function handleChange(event) {
const {name, value, type, checked} = event.target
setFormData(prevFormData => {
return {
...prevFormData,
[name]: type === "checkbox" ? checked : value
}
})
}
now we are going to add inputs look at note 2 again and now you are ready
import React from "react"
export default function Form() {
const [formData, setFormData] = React.useState(
{
firstName: "",
lastName: "",
email: "",
comments: "",
isFriendly: true,
employment: "",
favColor: ""
}
)
function handleChange(event) {
const {name, value, type, checked} = event.target
setFormData(prevFormData => {
return {
...prevFormData,
[name]: type === "checkbox" ? checked : value
}
})
}
function handleSubmit(event) {
event.preventDefault()
// submitToApi(formData)
console.log(formData)
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="First Name"
onChange={handleChange}
name="firstName"
value={formData.firstName}
/>
<input
type="text"
placeholder="Last Name"
onChange={handleChange}
name="lastName"
value={formData.lastName}
/>
<input
type="email"
placeholder="Email"
onChange={handleChange}
name="email"
value={formData.email}
/>
<textarea
value={formData.comments}
placeholder="Comments"
onChange={handleChange}
name="comments"
/>
<input
type="checkbox"
id="isFriendly"
checked={formData.isFriendly}
onChange={handleChange}
name="isFriendly"
/>
<label htmlFor="isFriendly">Are you friendly?</label>
<br />
<br />
<fieldset>
<legend>Current employment status</legend>
<input
type="radio"
id="unemployed"
name="employment"
value="unemployed"
checked={formData.employment === "unemployed"}
onChange={handleChange}
/>
<label htmlFor="unemployed">Unemployed</label>
<br />
<input
type="radio"
id="part-time"
name="employment"
value="part-time"
checked={formData.employment === "part-time"}
onChange={handleChange}
/>
<label htmlFor="part-time">Part-time</label>
<br />
<input
type="radio"
id="full-time"
name="employment"
value="full-time"
checked={formData.employment === "full-time"}
onChange={handleChange}
/>
<label htmlFor="full-time">Full-time</label>
<br />
</fieldset>
<br />
<label htmlFor="favColor">What is your favorite color?</label>
<br />
<select
id="favColor"
value={formData.favColor}
onChange={handleChange}
name="favColor"
>
<option value="red">Red</option>
<option value="orange">Orange</option>
<option value="yellow">Yellow</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
<option value="indigo">Indigo</option>
<option value="violet">Violet</option>
</select>
<br />
<br />
<button>Submit</button>
</form>
)
}

add category to a new task with react

i want to add a category when i add a task
i try to add this with query selector the state is assigned but it don't appear no the new task
class AddTodo extends Component{
state={
content: '',
importances:''
}
handleChange = (e, importances) => {
var test=document.querySelector('select').value
importances=test
this.setState({
content: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault();
this.props.addTodo(this.state)
this.setState({content:''});
}
render(){
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>Add new Todo:</label>
<input type="text" onChange={this.handleChange} value={this.state.content}/>
<select name="category" >
<option value="Très important">Très important</option>
<option value="Important">Important</option>
<option value="A faire">A faire</option>
</select>
<button className="addBtn">Add</button>
</form>
</div>
)
}
}
export default AddTodo
finaly i made this :
and add on the state by default property
selectedOption: 'Très important',
<form onSubmit={this.handleSubmit}>
<label>Add new Todo:</label>
<input type="text" onChange={this.handleChange} value={this.state.content} />
<select value={current_option} onChange={(e) => this.setState({ selectedOption: e.target.value })} >
Add onChange event listener to the select component and handle the value of the select.
<select value={current_option} onChange={() => onOptionChanged()} >

Why are my React form submission values undefined?

I'm trying to create a form and store the values of it in state – so far, so reasonable.
I think I've set it all up right, but when I return the contents of state, each and every field comes back undefined. I don't doubt that there's something simple I've overlooked in setting it up, but I can't for the life of me see what it is...
Can someone put me out of my misery?
handleAddProperty = (event) => {
event.preventDefault();
console.log(this.state.fields);
console.log(this.state.fields.type);
};
handleFieldChange = (event) => {
this.setState({
fields: {
title: event.target.value.title,
type: event.target.value.type,
bedrooms: event.target.value.bedrooms,
bathrooms: event.target.value.bathrooms,
price: event.target.value.price,
city: event.target.value.city,
email: event.target.value.email,
},
});
};
render() {
return (
<div className="addproperty">
<form onSubmit={this.handleAddProperty}>
<button type="submit">Add</button>
<input name="title" value={this.state.fields.title} onChange={this.handleFieldChange} />
<select name="type" value={this.state.fields.type} onChange={this.handleFieldChange}>
<option value={this.state.fields.type}>Flat</option>
<option value={this.state.fields.type}>Detached</option>
<option value={this.state.fields.type}>Semi-Detached</option>
<option value={this.state.fields.type}>Terraced</option>
<option value={this.state.fields.type}>End of Terrace</option>
<option value={this.state.fields.type}>Cottage</option>
<option value={this.state.fields.type}>Bungalow</option>
</select>
<input name="bedrooms" value={this.state.fields.bedrooms} onChange={this.handleFieldChange} />
<input name="bathrooms" value={this.state.fields.bathrooms} onChange={this.handleFieldChange} />
<input name="price" value={this.state.fields.price} onChange={this.handleFieldChange} />
<select name="city" value={this.state.fields.city} onChange={this.handleFieldChange}>
<option value={this.state.fields.city}>Manchester</option>
<option value={this.state.fields.city}>Leeds</option>
<option value={this.state.fields.city}>Sheffield</option>
<option value={this.state.fields.city}>Liverpool</option>
</select>
<input name="email" value={this.state.fields.email} onChange={this.handleFieldChange} />
</form>
</div>
);
}
}
You should access to event.target.value rather then to event.target.value[key] because handleFieldChange function triggers for each input field (when they change their state) and for each of that triggers event.targetis referring to a different input field (basically the input which has been changed).
To update the state you can use event.target.name as a key to your form object members inside the component state. The code might look like the following:
constructor(props) {
super(props);
this.state = { form: { name: 'Jane' } };
}
handleFieldChange(event) {
// This will update specific key in your form object inside the local state
this.setState({
form: Object.assign({}, this.state.form, {
[event.target.name]: event.target.value,
}),
});
}
<input
name="test"
type="text"
onChange={e => this.handleFieldChange(e)}
/>

Resources