Why are my React form submission values undefined? - reactjs

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

Related

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

Dynamic Forms in Netlify

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

Return value from a checkbox being checked in a mixed field react form

Ideally I want to use a single handleChanges function to set state for every field in my form. Right now, I don't really know how to make it so the checkboxes return a specific value, either true or false, or maybe even a string of "yes" or "no" depending on if they are toggled. I don't know where to put the logic for it.
Here is a simplified example of my form:
const ComponentFunction = props => {
const [fields, setFields] = useState({
inputTypeText: "",
textArea: "",
selectDropdown: "",
checkbox: "",
});
const handleChanges = event => {
setField({ ...fields, [event.target.name]: event.target.value });
};
const submitForm = event => {
event.preventDefault();
props.postForm(fields);
};
return (
<>
<form onSubmit={submitForm}>
<label htmlFor="inputTypeText">Input Type Text</label>
<input
id="title"
type="text"
name="title"
onChange={handleChanges}
value={property.title}
/>
<label htmlFor="textArea">Text Area</label>
<textarea
id="textArea"
rows="4"
cols="50"
name="textArea"
value={property.textArea}
onChange={handleChanges}
/>
<label htmlFor="selectDropdown">Select Dropdown</label>
<select onChange={handleChanges} value={property.selectDropdown} name="selectDropdown" id="select">
<option value="">--Select One--</option>
<option value="Stuff">Stuff</option>
</select>
<label htmlFor="checkbox>Check Box</label>
<input
id="checkbox"
name="checkbox"
type="checkbox"
value={property.checkbox}
onChange={handleChanges}
/>
<button type="submit">Submit</button>
</form>
</>
}
I ended up solving this by adding an additional handler just for checkboxes. Not sure if this is totally kosher, but it's pretty clean and worked perfectly.
This is the handler:
const handleClick = event => {
setFields({ ...fields, [event.target.name]: (event.target.checked ? "Checked" : "Not Checked") });
};
This is the checkbox:
<label htmlFor="checkbox">Check Box</label>
<input
id="checkbox"
name="checkbox"
type="checkbox"
onClick={handleClick}
/>

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

Use onSubmit to setState for the multiple inputs?

I want to take the data from these two inputs within my form and set one as state.name, and the other as state.option. Is there a way to set them upon using the form submit button rather than using individual onChange handlers to set each of those states? If not, it feels awkward having a submit button that doesn't really do anything.
onTextChange(name) {
this.setState({ name });
console.log(this.state.name);
}
onOptionChange(option) {
this.setState({ realm });
console.log(this.state.option);
}
onSubmitForm(event) {
event.preventDefault();
}
render() {
return (
<div>
<h3>Enter Name:</h3>
<form onSubmit={event => this.onSubmitForm(event.target.value)}>
<input
type="text"
placeholder="Name"
value={this.state.name}
onChange={event => this.onTextChange(event.target.value)}
/>
Select Option:
<select onChange = {event => this.onOptionChange(event.target.value)}>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
<input type="submit" />
</form>
In React, you have to control what happens in form/input in state and setState to change it. That's call Single source of truth
Welcome to React :)

Resources