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
Related
I am trying to prepopulate a Formik with JSON data. The data appears fine and it is returned. I have two issues.
I am using ASP.NET Core as the back end. Should I use values or initialValues as the variable? When I use initialValues, I get assign fields. The labels are still populated.
The labels are still populated, after I send a default value. All the fields are read only and I cannot edit them:
Here is my code:
function OrdersToReporting() {
const [initialValues, setInitialValues] = useState<OrdersToReportingViewModels>({ });
useEffect(() => {
agent.Settings.getOrdersSettings().then((userList) => {
let solutionName = (new URLSearchParams(window.location.search)).get("SolutionName");
let solutionWebsite = (new URLSearchParams(window.location.search)).get("SolutionWebsite");
userList.data.SolutionName = solutionName;
userList.data.SolutionWebsite = solutionWebsite;
setInitialValues(userList.data);
});
}, []);
const handleSubmit = async (
values: any,
{ resetForm, setErrors, setStatus, setSubmitting }: any
) => {
try {
await timeOut(1500);
agent.Settings.saveOrdersSettings(values);
resetForm();
setStatus({ sent: true });
setSubmitting(false);
} catch (error: any) {
setStatus({ sent: false });
setErrors({ submit: error.message });
setSubmitting(false);
}
};
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{({
errors,
handleBlur,
handleChange,
handleSubmit,
isSubmitting,
touched,
values,
status,
}) => (
{isSubmitting ? (
<Box display="flex" justifyContent="center" my={6}>
<CircularProgress />
</Box>
) : (
<form onSubmit={handleSubmit}>
<Grid container spacing={6}>
<Grid item md={6}>
<TextField
name="SolutionName"
label="Solution Name"
value={initialValues.SolutionName}
fullWidth
onBlur={handleBlur}
onChange={handleChange}
variant="outlined"
my={2}
/>
<TextField
name="SolutionWebsite"
label="Solution Website"
value={initialValues.SolutionWebsite}
fullWidth
onBlur={handleBlur}
onChange={handleChange}
variant="outlined"
my={2}
/>
<TextField
name="lastDownloadUtc"
label="Last Download Date Start"
value={initialValues.LastDownloadUtc}
fullWidth
onBlur={handleBlur}
onChange={handleChange}
variant="outlined"
my={2}
/>
</Grid>
<Grid item md={6}>
<TextField
name="lastDownloadUtcEnd"
label="Last Download Date End"
value={initialValues.LastDownloadUtcEnd}
fullWidth
onBlur={handleBlur}
onChange={handleChange}
variant="outlined"
my={2}
/>
</Grid>
</Grid>
<Button
type="submit"
variant="contained"
color="primary"
mt={3}
>
Save changes
</Button>
</form>
)}
)}
</Formik>
);
}
For your questions,
You should use initialValues to assign default values to your fields.
since you are getting the data from some function, there might be a delay in setting initialValues. So pass enableReinitialize={true} in formik props.
You were not able to change the values because you set
value={initialValues.fieldName} // always value will be this unless you change initialValues state
for every textfield. Always this will be same even if you edit.
Just remove the value property formik will handle it by default.
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>
I have a form that i'm controlling by formik, when i fill all the fields and press the buttom submit, the function onSubmit is called and my form have this values reseted.
Sometimes my data is incorrect (like a duplicate email) and i need to persist this data.
How i can do this?
This is my code:
const schema = Yup.object().shape({
login: Yup.string()
.email('Email não possui formato válido')
.required('Informe o email!'),
password: Yup.string().required('Informe a senha!'),
})
const formik = useFormik({
initialValues: {
login: '', password: '', inactive: false
},
validationSchema: schema,
onSubmit: values => {
registerUser(values)
}
})
return (
<form onSubmit={formik.handleSubmit} className={classes.form} noValidate>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography className={classes.observation} component="h6">* Necessário preenchimento do cadastro geral para liberar permissão de telas</Typography>
</Grid>
<Grid item xs={5}>
<TextField
value={formik.values.login}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={formik.touched.login ? formik.errors.login : ""}
error={formik.touched.login && Boolean(formik.errors.login)}
variant="outlined"
margin="normal"
required
fullWidth
id="email"
label="E-mail"
name="login"
autoComplete="email"
/>
</Grid>
<Grid item xs={5}>
<TextField
value={formik.values.password}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={formik.touched.password ? formik.errors.password : ""}
error={formik.touched.password && Boolean(formik.errors.password)}
variant="outlined"
margin="normal"
required
fullWidth
type="password"
id="password"
label="Senha"
name="password"
/>
</Grid>
<Grid item xs={2}>
<FormControlLabel
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.inactive}
control={<Switch color="primary" />}
label="Inativo"
labelPlacement="top"
/>
</Grid>
<Grid item xs={3}>
<Button fullWidth
variant="contained"
color="primary"
type="submit"
>
Cadastrar
</Button>
</Grid>
</Grid>
</form>
);
Use the Form from formik, and the default is to not reset on submit:
import { Formik, Form } from "formik";
function DemoComp(){
return(
<Formik
initialValues={{ fieldOneVal: "" }}
onSubmit={async (formsData, {setSubmitting, resetForm}) => {
setSubmitting(true)
// async request
// --> if wanted to reset on submit: resetForm();
console.log(formsData)
setSubmitting(false)
}}
>
{({ values, isSubmitting, handleChange, handleBlur, handleSubmit }) => (
<Form>
<input
type="text"
name="fieldOneVal"
value={values.fieldOneVal}
onChange={handleChange}
onBlur={handleBlur}
/>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
)
}
You need to update the following line:
<form onSubmit={(e) => { e.preventDefault(); formik.handleSubmit(e)}} className={classes.form} noValidate>
You need to define the function to be async and as async response handling is not mention in your code so it's hard to give exact solution
<Formik
initialValues={{ fieldOneVal: formvalues.fieldOneVal }}
onSubmit={async (values, { resetForm }) => {
// setFormvalues(({fieldOneVal:values.fieldOneval}) don't know why you're manipulating state
const resp=await someAsyncFunc()
if (resp){
reset the state to initial values}
else{
pop up modal display with errors}
}}
>
I faced the same problem formik resetting the form before i get a response from the server.This solution helped me.
import { Formik, Form } from "formik";
function DemoComp(){
const [formvalues,setFormvalues]= useState({
{ fieldOneVal: "" }
)
return(
<Formik
initialValues={{ fieldOneVal: formvalues.fieldOneVal }}
onSubmit={(values, { resetForm }) => {
setFormvalues(({fieldOneVal:values.fieldOneval})
// async request
if sucess{
reset the state to initial values}
else{
pop up modal display with errors}
}}
>
{({ values, isSubmitting, handleChange, handleBlur, handleSubmit }) => (
<Form>
<input
type="text"
name="fieldOneVal"
value={values.fieldOneVal}
onChange={handleChange}
onBlur={handleBlur}
/>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
)
}
So, I'm trying to create form with Formik and Material UI. For all the fields everything is working as it should but the problem is with Autocomplete. I cannot find the way to populate the field from the localStorage. I tried everything, from putting the value props, inputValue, defaultValue, etc but nothing seems to work.
import React from 'react'
import { Grid } from '#material-ui/core'
import { Autocomplete } from '#material-ui/lab'
import { Formik, Form, Field } from 'formik'
import { TextField } from 'formik-material-ui'
import * as yup from 'yup'
import { nationality } from '../../constants/nationality'
import Button from '../Button/Button'
export default function ForeignAddress () {
let localStorageData = localStorage.getItem('foreignAddress'),
retrivedData = JSON.parse(localStorageData)
const handleNextClick = () => {
console.log('clicked next')
}
const handleBackClick = () => {
console.log('clicked back')
}
const validationSchema = yup.object({
streetName: yup.string().required('Street name is required'),
streetNumber: yup.string().required('Street number is required'),
postalCode: yup.string().required('Postal code is required'),
city: yup.string().required('City is required'),
country: yup.string().required('Country is required'),
})
console.log(retrivedData)
return (
<React.Fragment>
<div className="pages-wrapper address">
<Formik
initialValues={retrivedData ? retrivedData : {streetName: '', streetNumber: '', postalCode: '', city: '', coAddress: '', country: ''}}
onSubmit={(data) => {
console.log(data)
localStorage.setItem('foreignAddress', JSON.stringify(data))
handleNextClick()
}}
validationSchema={validationSchema}
>
{({setFieldValue}) => (
<Form>
<Grid container spacing={3}>
<Grid item xs={12} md={8}>
<Field component={TextField} name="streetName" label="Street Name" variant="outlined" fullWidth />
</Grid>
<Grid item xs={12} md={4}>
<Field component={TextField} name="streetNumber" label="Street Number" variant="outlined" fullWidth />
</Grid>
<Grid item xs={12} md={4}>
<Field component={TextField} name="postalCode" label="Postal Code" variant="outlined" fullWidth />
</Grid>
<Grid item xs={12} md={8}>
<Field component={TextField} name="city" label="City" variant="outlined" fullWidth />
</Grid>
<Grid item xs={12} md={6}>
<Field component={TextField} name="coAddress" label="C/O Address" variant="outlined" fullWidth />
</Grid>
<Grid item xs={12} md={6}>
<Autocomplete
id="foreignCountry"
className="country-select"
name="country"
options={nationality}
getOptionLabel={option => option.label}
onChange={(e, value) => {
console.log(value)
setFieldValue("country", value.code)
}}
renderInput={params => (
<Field component={TextField} {...params} name="country" label="Country" variant="outlined" fullWidth/>
)}
/>
</Grid>
</Grid>
<div className="button-wrapper">
<Button label="Back" go="back" handleClick={handleBackClick}/>
<Button label="Next" go="next" type="submit" />
</div>
</Form>
)}
</Formik>
</div>
</React.Fragment>
)
}
EDIT:
Thanks to #Vencovsky i was able to get it done
in case someone in the future needs this here is the working code.
Just change Autocomplete component to
<Autocomplete
id="foreignCountry"
className="country-select"
name="country"
options={nationality}
getOptionLabel={option => option.label}
defaultValue={values.country}
onChange={(e, value) => {
console.log(value)
setFieldValue("country", value)
}}
renderInput={params => (
<Field component={TextField} {...params} name="country" label="Country" variant="outlined" fullWidth/>
)}
/>
and in the Formik props just add values prop
{({setFieldValue, values}) => (
<Form>,...
Edit:
There is a few thing you need to change.
First you can't just store the code to load it later, you need to store everything (the hole value object) from the options.
Change the initial value of country: '' to country: {code: "", label: "", phone: ""} which is all the default value of the options.
Then to load the value correctly you should pass value={values.country} where values comes from formik props.
And on onChange you should save the hole value onChange={(e, value) => {setFieldValue("country", value); }}
But you are also importing and using some wrong things like
<Field
component={TextField}
{...params}
name="country"
label="Country"
variant="outlined"
fullWidth
/>
Where Field is form formik and TextField from formik material ui.
Not sure why you use it like that, but I have changed it.
Here is a working example
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>