so i'm creating my portfolio and i am now on the final page, the contact page. I have it almost finished. I have it hooked up with emailjs and i receive emails with the message inputted as expected.
The problem i'm having is, when the form is submitted, i don't know how to clear the UI input fields. I could disregard using e.preventDefault(), however, i would like to keep that, as i want to style the page if the desired result has been achieved(message sent), or if an error has occurred. I would like to mention that i had used state for the name, email and message beforehand, however, i was unable to use the state variables in conjunction with the emailjs syntax, more specifically, with the "e.target" section. When the form is submitted, the result is the message being sent to my email, with the text inputted by the user still in the input fields.
The code is below, with some names left as hidden for privacy reasons:
import React, { useState } from 'react'
import { Box, Grid, Typography, Button} from '#material-ui/core'
import Navbar from './Navbar';
import Styles, { InputField } from './Styles'
import SendIcon from '#material-ui/icons/Send'
import emailjs from 'emailjs-com'
function Contact() {
const classes = Styles()
function sendEmail(e) {
e.preventDefault()
emailjs.sendForm('gmail', 'hidden', e.target, 'hidden')
.then((result) => {
console.log(result.text);
result.text ==="OK" ? console.log("it worked") : console.log("didnt work")
}, (error) => {
console.log(error.text);
});
}
return (
<Box component='div'>
<Navbar/>
<Grid container justify='center'>
<Box component='form' className={classes.contactContainer} onSubmit={sendEmail}>
<Typography variant='h5' className={classes.contactHead}>Contact Me</Typography>
<InputField
id="name"
name="name"
fullWidth={true}
label="Name"
variant="outlined"
margin='dense'
size='medium'
/>
<br/>
<InputField
id="email"
name="email"
fullWidth={true}
label="Email"
variant="outlined"
margin='dense'
size='medium'
/>
<br/>
<InputField
id="message"
name="message"
fullWidth={true}
label="Enter Message Here"
multiline
rows={8}
variant="outlined"
margin='dense'
size='medium'
/>
<br/>
<Button
type="submit"
variant='outlined'
fullWidth={true}
endIcon={<SendIcon/>}
className={classes.contactButton}>
Contact Me
</Button>
</Box>
</Grid>
</Box>
)
}
export default Contact
For the simplest way to do it in your code, use useState to declare initial value of the fields such as:
const [name, setName] = useState("");
Then you need to set the "value" param in your InputField component, eg:
<InputField
id="name"
name="name"
fullWidth={true}
label="Name"
variant="outlined"
margin='dense'
size='medium'
value={name}
/>
And after receiving the result in emailjs.sendForm, use setName to reset the value of the name field, eg:
setName("")
Use the similar method for other fields.
Thank you for the answer, it helped, however did not fully fix the problem. That being said, i was able to find a solution. I used the onChange param and passed through a function which changes the state AND the value. Also, after receiving the result in emailjs.sendForm, i reset the value of all the fields.
const handleChange = (event) => {
event.target.name=="name"
? setName(event.target.value)
: event.target.name=="email"
? setEmail(event.target.value)
: event.target.name=="message"
? setMessage(event.target.value)
: console.log("error")
};
function sendEmail(e) {
e.preventDefault()
emailjs.sendForm('gmail', 'hiddenForPrivacy', e.target, 'hiddenForPrivacy')
.then((result) => {
console.log(result.text);
result.text ==="OK" ? console.log("it worked") : alert("didnt work")
setName("")
setMessage("")
setEmail("")
}, (error) => {
console.log(error.text);
});
}
The input fields now look like this:
return (
<Box component='div'>
<Navbar/>
<Grid container justify='center'>
<Box component='form' className={classes.contactContainer} onSubmit={sendEmail}>
<Typography variant='h5' className={classes.contactHead}>Contact Me</Typography>
<InputField
id="name"
name="name"
fullWidth={true}
label="Name"
variant="outlined"
margin='dense'
size='medium'
onChange={(e) => handleChange(e)}
value={name}
/>
<br/>
<InputField
id="email"
name="email"
fullWidth={true}
label="Email"
variant="outlined"
margin='dense'
size='medium'
onChange={(e) => handleChange(e)}
value={email}
/>
<br/>
<InputField
id="message"
name="message"
fullWidth={true}
label="Enter Message Here"
multiline
rows={8}
variant="outlined"
margin='dense'
size='medium'
onChange={(e) => handleChange(e)}
value={message}
/>
<br/>
<Button
type="submit"
variant='outlined'
fullWidth={true}
endIcon={<SendIcon/>}
className={classes.contactButton}>
Contact Me
</Button>
</Box>
</Grid>
</Box>
Related
I created a component for my contact form, and am trying to import it into a on my contact page. When I do this, the styling of the inputs gets totally thrown off... they show up as tiny boxes on the right of the screen. What am I missing here?
Messed up form inputs:
This is a Next.js app. I've also noticed that on the initial load of the page, this issue doesn't occur... it's only when I refresh.
Here's my form component:
import {
Button,
FormControl,
InputLabel,
OutlinedInput} from '#material-ui/core';
import styles from '../styles/ContactForm.module.css';
import { useState } from 'react';
const ContactForm = () => {
const [name, setName] = useState('');
const [phone, setPhone] = useState('');
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');
const handleSubmit = (event) => {
event.preventDefault();
console.log(`
Name: ${name}
Phone: ${phone}
Email: ${email}
Message: ${message}
`);
};
return (
<form id={styles.contactForm} onSubmit={handleSubmit}>
<FormControl
fullWidth
className={styles.formInput}
variant="outlined"
color="primary">
<InputLabel htmlFor="name">Name</InputLabel>
<OutlinedInput
color="primary"
id="name"
labelWidth={50}
value={name}
onChange={(event) => {setName(event.target.value)}}
required/>
</FormControl>
<FormControl
fullWidth
className={styles.formInput}
variant="outlined"
color="primary">
<InputLabel htmlFor="phone">Phone</InputLabel>
<OutlinedInput
color="primary"
id="phone"
labelWidth={50}
value={phone}
onChange={(event) => {setPhone(event.target.value)}}
required/>
</FormControl>
<FormControl
fullWidth
className={styles.formInput}
variant="outlined"
color="primary">
<InputLabel htmlFor="email">Email</InputLabel>
<OutlinedInput
color="primary"
id="email"
labelWidth={50}
value={email}
onChange={(event) => {setEmail(event.target.value)}}
required/>
</FormControl>
<FormControl
fullWidth
className={styles.formInput}
variant="outlined"
color="primary">
<InputLabel htmlFor="message">Message</InputLabel>
<OutlinedInput
color="primary"
id="message"
labelWidth={50}
multiline rows={8}
value={message}
onChange={(event) => {setMessage(event.target.value)}}
required/>
</FormControl>
<Button
variant="contained"
color="primary"
type="submit">Submit</Button>
</form>
);
};
export default ContactForm;
Here is my contact page component (where the inputs get messed up). The only CSS I have is making .formWrapper have a width of 500px:
import ContactForm from '../components/ContactForm';
import styles from '../styles/contact.module.css';
const contact = () => {
return (
<main id={styles.contact}>
<section className={styles.formWrapper}>
<ContactForm />
</section>
</main>
);
};
export default contact;
Any ideas on why my inputs are doing this?
Turns out that the issue here was the fact that NextJS renders on the server, so the MUI classes were not matching up correctly when I re-rendered the page. I was able to fix the problem by following this documentation:
https://material-ui.com/guides/server-rendering/
How can I code this where the textfield's original state will be disabled and then when typed in, it will be enabled. Then that textfield will be the only one be updated when submitted?
<form onSubmit={handleSubmit}>
<TextField
id="input"
margin="dense"
type="text"
label="Name"
placeholder={user.displayName}
value={displayName}
fullWidth
onChange={(e) => setdisplayName(e.target.value)}
/>
<TextField
disabled
margin="dense"
type="text"
label="address"
value={address}
fullWidth
onChange={(e) => setAddress(e.target.value)}
/>
<Button type="submit">Update</Button>
</form>
Even though you disable one input, It still re-render everytime you typing (because you setState on change). You can use ref like this to prevent unnecessary render.
const nameRef = useRef(null);
const addressRef = useRef(null);
const handleSubmit = (e) => {
e.preventDefault();
console.log(nameRef.current.value);
console.log(addressRef.current.value);
};
return (
<div>
<form onSubmit={handleSubmit}>
<TextField
id="input"
margin="dense"
type="text"
label="Name"
placeholder={user.displayName}
fullWidth
inputRef={nameRef}
/>
<TextField
margin="dense"
type="text"
label="address"
fullWidth
inputRef={addressRef}
/>
<Button type="submit">Update</Button>
</form>
)
You can add a validation on handleSubmit because if a textfield is disabled, you need another action to make it enable and it's not ok for the user also.
You can try to add something like:
const handleSubmit = () => {
if ((displayName === "" || displayName == null) && (address == "" || address === null) {
//show error for user that both fields have to be completed
} else {
//do the form submit
}
}
I have a sample code for Signing page as follows, When I am typing the username and passwords inside the text fields with every character the onClick event of the button is triggered and I can see the output of console.log(). Why it is triggered?
class Signin extends React.Component {
state = {
username: null,
password: null,
}
render () {
const { t, classes } = this.props;
return (
<div >
<div >
<div >
<Card>
<CardContent>
<form>
<TextField
inputProps={{ style:{fontSize:20, textAlign:'center', direction:'ltr'} }}
value={this.state.username}
onChange={e => this.setState({username: e.target.value})}
id="username"
label={t("Username")}
className={classes.textField}
fullWidth
margin="normal"
/>
<TextField
inputProps={{ style:{fontSize:20, textAlign:'center', direction:'ltr'} }}
value={this.state.password}
onChange={e => this.setState({password: e.target.value})}
id="password"
label={t("Password")}
className={classes.textField}
type="password"
fullWidth
margin="normal"
/>
<Button variant="raised" color="primary" fullWidth type="submit" onClick={console.log(this.state.username,this.state.password)} >{t("Login")}</Button>
</form>
</CardContent>
</Card>
</div>
</div>
</div>
);
}
}
Change your console.log from:
onClick={console.log(this.state.username,this.state.password)}
to like this below:
onClick={() => console.log(this.state.username,this.state.password)}
try this
it is happening because It depends on where exactly are you using the Arrow function. If the Arrow function is used in the render method, then they create a new instance every time render is called just like how bind would work.
<Button
variant="raised"
color="primary"
fullWidth type="submit"
onClick={()=>{console.log(this.state.username,this.state.password)} >{t("Login")}}</Button>
I am trying to add a new user to my database using the axios post method, with a form that I made with material ui, the application runs well does not mark me any console errors or in the browser and if it shows me the form but I can't write the TextFields.
Before adding the functions, I could write well but after adding Onchange and value I no longer let myself write
What could be happening?
Component
import React, { useState } from "react";
import axios from 'axios';
//useState hook create a state named user with initial state set to an object with name, lastname and age properties set to empty strings
function UserAdd(props) {
const initialState = { name: '', lastname: '', age:0 }
const [user, setUser] = useState(initialState)
function handleChange(event) {
setUser({...user, [event.target.name]: event.target.value})
}
function handleSubmit(event) {
event.preventDefault();
if(!user.name || !user.lastname || !user.age) return
async function postUser() {
try {
const response = await post('/api/addUser', user);
props.history.push(`/user/${response.data._id}`);
} catch(error) {
console.log('error', error);
}
}
postUser();
}
function handleCancel() {
props.history.push("/users");
}
return (
<div style={{ padding: 16, margin: 'auto', maxWidth: 1000 }}>
<Typography variant="h4" align="center" component="h1" gutterBottom>
Add User
</Typography>
<form onSubmit={handleSubmit} className={classes.container} >
<TextField
label="Name"
className={classes.textField}
value={user.name}
onChange={handleChange}
margin="normal"
variant="outlined"
/>
<TextField
id="filled-read-only-input"
label="Lastname "
className={classes.textField}
value={user.lastname}
onChange={handleChange}
margin="normal"
variant="outlined"
/>
<TextField
required
id="filled-required"
label="Age"
className={classes.textField}
value={user.age}
onChange={handleChange}
margin="normal"
variant="outlined"
/>
<Grid item style={{ marginTop: 30 }}>
<Button
variant="contained"
color="primary"
type="submit">
Add
</Button>
</Grid>
<Grid item style={{ marginTop: 30 }}>
<Button
onClick={handleCancel}
variant="contained"
type="cancel">
Cancel
</Button>
</Grid>
</form>
</div>
);
}
export default UserAdd;```
It looks like you need to have a name attribute to pass with event.target.name. Not sure what props TextField takes, but I assume it would be something like this:
<TextField
name="name" // Add this
label="Name"
className={classes.textField}
value={user.name}
onChange={handleChange}
margin="normal"
variant="outlined"
/>
Similarly, you'd need to have a name prop for the other input components.
I have the following Formik form
<Formik
initialValues={{
email: '',
password: ''
}}
onSubmit={(values, { setSubmitting }) => {
console.log(JSON.stringify(values));
setSubmitting(true);
fetch('/login', {
method: 'POST',
body: JSON.stringify(values),
headers: {
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then(response => {
console.log(response);
setSubmitting(false);
if (!response.success) return showAlert();
login(response.user);
history.push('/');
})
.catch(console.log);
}}
>
{({ isSubmitting }) => (
<Form className={classes.form}>
<TextField
required
variant="outlined"
margin="normal"
fullWidth
label="Email"
name="email"
autoFocus
/>
<TextField
required
variant="outlined"
margin="normal"
fullWidth
name="password"
label="Wachtwoord"
type="password"
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
disabled={isSubmitting}
>
Log in
</Button>
<Grid container>
<Grid item xs>
<Link href="/register" variant="body2">
{'Nog geen account? Registreer nu!'}
</Link>
</Grid>
</Grid>
</Form>
)}
</Formik>
For some reason the values in the onSubmit are empty, how can I set it to the values inside the form? The only difference between this form and another one I got to work is that this one does not have a validation schema but I can not imagine that is required
You should take a look at the example in formik's docs.
TextField isn't connect to Formik. When the value of TextField changes, Formik don't change, you need Field (import { Field } from 'formik') that will render TextField.
Example for email
<Field
name="email"
render={({ field /* _form */ }) => (
<TextField
required
variant="outlined"
margin="normal"
fullWidth
label="Email"
autoFocus
{...field}
/>
)}
/>
If you are using a external input, you should use this way.
You need to pass this
onChangeText={handleChange('email')}
onBlur={handleBlur('email')}
value={values.email}
to the Textinput, and for Password aswell.
Your Textinput doesn't track any changes or submission.
Check here