I currently have a working hook, that checks the onChange of input fields, then spreads (I think that is the term for ...) them into a variable and send's them to a Lamda function to send a email with sendgrid. All of that is working.
However if I add a input type of checkbox I am a little lost on the logic side. Because I am checking for the target's name and pairing that with it's value. Well the checkbox has a checked state not a value.
I assume I have to do something like e.target.checked in my setFormState however how do I do that as well as the e.target.value and still spread it to ...formState?
I am a react noob.
Here is my react state and the change event and submit event
const [formState, setFormState] = React.useState({
name: "",
package: `${data.datoCmsPricing.title}`,
email: "",
subject: "",
weightLoss:"",
message: "",
})
const onChange = (e) => {
setFormState({...formState, [e.target.name]: e.target.value });
}
const submitForm = async (e) => {
e.preventDefault();
console.log("test");
try{
const response = await fetch("/.netlify/functions/sendmail", {
method: "POST",
body: JSON.stringify(formState),
})
if (!response.ok) {
console.log(response);
return
}
console.log("success email");
} catch(e){
console.log("error");
}
}
Here is my form code
<form onSubmit={submitForm}>
<label>
Name
<input
type="text"
name="name"
value={formState.name}
onChange={onChange}
/>
</label>
<label>
Email
<input
type="email"
name="email"
value={formState.email}
onChange={onChange}
/>
</label>
<label>
Subject
<input
type="text"
name="subject"
value={formState.subject}
onChange={onChange}
/>
</label>
<div>
<h3>Reasons for wanting to train</h3>
<label>
Weight Loss
<input
type="checkbox"
name="weightLoss"
checked={formState.weightLoss}
onChange={onChange}
/>
</label>
</div>
<label>
message
<textarea
name="message"
value={formState.message}
onChange={onChange}
/>
</label>
<button type="submit">Submit</button>
</form>
In your change handler, detect if it's a checkbox that is not checked, then set the state accordingly:
if (e.target.type === 'checkbox' && !e.target.checked) {
setFormState({...formState, [e.target.name]: ''});
} else {
setFormState({...formState, [e.target.name]: e.target.value });
}
Related
So I am trying to make a textfield component in React that is highly reusable but whenever I try to access event.target.name or event.target.value I get empty data.
Is there any way to get this code to work?
function LoginForm() {
const [form, setValues] = useState({
username: "",
password: ""
});
const printValues = e => {
e.preventDefault();
console.log(form.username, form.password);
};
const updateField = e => {
setValues({
...form,
[e.target.name]: e.target.value
});
};
const TextField = props => {
return(
<label>
React Component:
<input
value={props.value}
name='react'
onChange={e => props.change(e)}
/>
</label>
)
}
return (
<form onSubmit={printValues}>
<TextField
value={form.username}
name='username'
change={updateField}
/>
<br />
<TextField
value={form.password}
name='password'
change={updateField}
/>
<br />
<button>Submit</button>
</form>
);
}
This code is an example that I have gotten to work. Why does this code work but not the code above?
function LoginForm() {
const [form, setValues] = useState({
username: "",
password: ""
});
const printValues = e => {
e.preventDefault();
console.log(form.username, form.password);
};
const updateField = e => {
setValues({
...form,
[e.target.name]: e.target.value
});
};
return (
<form onSubmit={printValues}>
<label>
Username:
<input
value={form.username}
name="username"
onChange={updateField}
/>
</label>
<br />
<label>
Password:
<input
value={form.password}
name="password"
type="password"
onChange={updateField}
/>
</label>
<br />
<button>Submit</button>
</form>
);
}
your child component has it's name prop hardcoded name='react' and that's because your [e.target.name]: e.targe.value statement is not working, use name={props.name} instead and it would solve the problem.
const TextField = props => {
return(
<label>
React Component:
<input
value={props.value}
name={props.name}
onChange={e => props.change(e)}
/>
</label>
)
}
The first code you show has a prop change instead of the onChange event listener in the TextField component.
I was looking here https://material-ui.com/es/components/text-fields/ because I never used material-ui, and in the example they still use the regular onChange.
So try changing change for onChange, as it is the only difference between both code examples besides the use of the component.
I had a component using classBased react, and it was working fine, I decided to switch to usestate and it stopepd working. Now the handlechange records the state in a random manner, but it doesnt work properly
My handlechange and the useState
const [state, setState] = useState({
email: "",
password: "",
wrongCombo: false,
serverError: false
})
const handleChange = (evt) => {
const target = evt.target;
const value = target.value;
const name = target.name;
console.log(name)
console.log(value)
setState({
[name]: value
});
}
My handlesubmit (it detects random values in that console.log, icant find the logic, but the log wont get both values as per inputed in the handlechange)
const handleSubmit = (event) => {
event.preventDefault();
const { email, password } = state;
console.log(state)
props.login(email, password, "login").
then(data => {
}).catch((err) => {
if (err == "Error: Request failed with status code 403") {
setState({
wrongCombo: true
}, () => {
})
} else if (err == "Network error") {
setState({
serverError: true
})
}
})
}
And this is my render
<div>
<form>
{state.wrongCombo ? <Alert variant="danger" dismissible onClose={handleDismiss}> Wrong email and password combination </Alert> : null}
{state.serverError ? <Alert variant="danger" dismissible onClose={handleDismiss}> Server Error </Alert> : null}
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" name="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email" onChange={handleChange} />
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" name="password" class="form-control" id="exampleInputPassword1" placeholder="Password" onChange={handleChange} />
</div>
<div className="text-center buttonContainer">
<button type="submit" class="btn btn-primary buttonLogin" onClick={handleSubmit}>Submit</button>
</div>
</form>
</div>
From the docs on useState:
unlike this.setState in a class, updating a state variable always replaces it instead of merging it.
You must replace all values in a useState object when updating them. You are only providing an object to the updater with one of the keys, so only that key will be retained.
A simple pattern for doing this would be to spread the previous state before passing your update:
setState(prev => ({...prev, [newKey]: newVal }));
I want create a function using with i can reset value in form inputs without submit. I tried create that function in App Component (resetFormFields) and pass it on props to Form Component. It's preety simply when I want to do this onSubmit (e.target.reset()) but I got stuck when I have to do it without submit, on a different element than the form. Can I do that without adding these values to state?
App:
class App extends Component {
state = {
people: [],
formMessages: [],
person: null
};
handleFormSubmit = e => {
e.preventDefault();
const form = e.target;
const name = form.elements["name"].value;
const username = form.elements["username"].value;
this.addPerson(name, email);
form.reset();
};
resetFormFields = () => {
return;
}
render() {
return (
<div className="App">
<Form formSubmit={this.handleFormSubmit}
reset={this.resetFormFields} />
</div>
);
}
Form:
const Form = props => (
<form className={classes.Form}
id="form"
onSubmit={props.formSubmit}>
<input autoFocus
id="name"
type="text"
defaultValue=""
placeholder="Name..."
/>
<input
id="email"
type="text"
defaultValue=""
placeholder="Email..."
/>
<Button
btnType="Submit"
form="form"
type='submit'>
Submit
</Button>
<label onClick={props.reset}>Reset fields</label>
</form> );
onHandleFormSubmit = (e) =>{
e.preventDefault();
e.target.reset();
}
You need to make your inputs controlled by passing the value you store in your state then you just have to reset the state values and your component value resets.
check this sample below
handleInputChange = (e) => {
let { name, value } = e.target;
this.setState({
...this.state,
inputs: {
[name]: value
}
});
}
your component will now look like
<input name='fullName' value={this.state.inputs.fullName} onChange={this.handleInputChange} />
Your reset function will just clear the state and your input field will be empty since it's controlled via state
resetInputFields = () => {
this.setState({ inputs: {} })
}
you should give set your input values based on component state, then just update the component state
class App extends Component {
state = {
people: [],
formMessages: [],
person: null,
name: "",
email: "",
};
updateState = (newState) => {
this.setState(newState);
}
handleFormSubmit = e => {
e.preventDefault();
this.addPerson(this.state.name, this.state.email);
form.reset();
};
resetFormFields = () => {
this.setState({name:"", email: ""});
}
render() {
return (
<div className="App">
<Form formSubmit={this.handleFormSubmit} updateState={this.updateState}
reset={this.resetFormFields} email={this.state.email} name={this.state.name} />
</div>
);
}
and then
const Form = props => (
<form className={classes.Form}
id="form"
onSubmit={props.formSubmit}>
<input autoFocus
id="name"
type="text"
defaultValue=""
value={this.props.name}
onChange={(e) => this.props.updateState({name: e.target.value})}
placeholder="Name..."
/>
<input
id="email"
type="text"
defaultValue=""
value={this.props.email}
onChange={(e) => this.props.updateState({email: e.target.value})}
placeholder="Email..."
/>
<Button
btnType="Submit"
form="form"
type='submit'>
Submit
</Button>
<label onClick={props.reset}>Reset fields</label>
</form> );
Here we have to write three functions to change the state of all input fields of form and in case if we have more fields e.g phone and address we have to write two more functions to change the state of those fields, i want to ask is there any way we can write only a single generic function to change the state of any field of this form rather than write separate function for every form field?
class SignUpForm extends React.Component {
constructor() {
super();
this.state = {
name: '',
email: '',
password: '',
};
}
handleNameChange = (evt) => {
this.setState({name: evt.target.value});
}
handleEmailChange = (evt) => {
this.setState({email: evt.target.value});
}
handlePasswordChange = (evt) => {
this.setState({password: evt.target.value});
}
render(){
return(
<form onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="Enter Name"
value={this.state.name}
onChange={this.handleNameChange}
/>
<input
type="text"
placeholder="Enter email"
value={this.state.email}
onChange={this.handleEmailChange}
/>
<input
type="password"
placeholder="Enter password"
value={this.state.password}
onChange={this.handlePasswordChange}
/>
<button disabled={isDisabled}>Sign up</button>
</form>
)
}
}
You could use the following pattern:
handleChange = (type, event) => {
this.setState({[type]: event.target.value});
}
You use handleChange like that:
<input
type="text"
placeholder="Enter Name"
value={this.state.name}
onChange={(event) => this.handleChange('name', event)}
/>
There's an even cleaner way to write this using currying:
handleChange = type => event => this.setState({[type]: event.target.value})
<input
type="text"
placeholder="Enter Name"
value={this.state.name}
id="name"
onChange={this.handleChange('name')}
/>
You could even use the element's ID attribute to avoid the instantiation of a new handler every time you render (since this.handleChange('name') would return a new instance of event => this.setState({name: event.target.value}) every render), and to avoid repeating yourself. However this may not be advisable since it means your element IDs must match your state keys.
handleChange = event => this.setState({[event.target.id]: event.target.value})
<input
type="text"
placeholder="Enter Name"
value={this.state.name}
id="name"
onChange={this.handleChange}
/>
I am trying to create a way for a user to multiply their form.
A user may add several "Name"/"Number" at one time. But they will only add it under one "Area"
How do I create a form where I can "duplicate" the input fields?
It will, in a way, simply "copy" the form so you can enter the data again.
This screenshot should explain:
I've also put it in a code pen:
http://codepen.io/yarnball/pen/qaQomG?editors=1010
var Postapi = React.createClass({
getInitialState: function() {
return {
area:'',
name: '',
number: '',
};
},
onChange(e) {
this.setState({[e.target.name]: e.target.value})
},
handleSubmit: function(e) {
e.preventDefault();
return fetch('http://localhost:8000/api/Data/', {
method: 'POST',
body: JSON.stringify({
area: this.state.area,
info:[
{name:this.state.name,
taglevel:this.state.number}
],
})
})
.then(function(res){ return res.json(); })
.then(function(data){ alert( JSON.stringify( data ) ) })
},
render: function() {
return (
<form onSubmit={this.handleSubmit}>
<input
name="area"
type="text"
placeholder="Area"
onChange={this.onChange}
/>
<input
name="name"
type="text"
placeholder="Name"
onChange={this.onChange}
/>
<input
name="number"
type="text"
placeholder="Number"
onChange={this.onChange}
/>
<button type="submit">Submit</button>
</form>
);
}
});
Working pen: http://codepen.io/pranesh-r/pen/qagmxX?editors=1010
You need state to maintain the number of input block to be displayed. When the user clicks add more, increment the state and render the n number of input blocks. This solves your first problem.
Handler
addMore(){
this.setState({
formItems: this.state.formItems + 1
})
},
HTML
<button type="button" onClick={this.addMore}>Add More</button>
Dynamic content
const fi = <div>
<input
name="name"
type="text"
placeholder="Name"
onChange={this.onChange}
/>
<input
name="number"
type="text"
placeholder="Number"
onChange={this.onChange}
/>
</div>
const formItem = [];
for (var i= 0; i< this.state.formItems; i++){
formItem.push(fi)
}
Use the formItem in the render.
To serialize the form, I've used jQuery .serialize(). If you don't wanna use jQuery you need to maintain the values of the input in the state for the each inputs. And during submit, get the value from the state and pass it to the server. This solves your second problem.
Hope this helps!