React input onChange lag (many textfields) - reactjs

I have quite a large form with many textfields inside, there are about 60-70 textfields inside.
Certainly it begins to lag and I know that the re-rendering could be a problem, also of the reason of having all values in the component state. I have no idea how to split it correctly or how I should solve that.
Main Component -> View
const [values, setValues] = useState({
value1: '',
...
value60: '',
});
return (
<div className={classes.root}>
<form>
<Grid container spacing={2}>
<Grid item xl={12}>
<Breadcrumb breadcrumb={breadcrumb}/>
</Grid>
<BlockInputBasic handleChange={handleChange.bind()} values={values}/>
<BlockInputTarget handleChange={handleChange.bind()} values={values}/>
</Grid>
</form>
</div>
);
Child Component
export default function BlockInputBasic(props) {
return (
<TextField
fullWidth
label="Name"
name="name"
onChange={props.handleChange}
required
value={props.values.name}
variant="outlined"
/>
<TextField
fullWidth
label="Name2"
name="name2"
onChange={props.handleChange}
required
value={props.values.name}
variant="outlined"
/>
....
<TextField
fullWidth
label="Adress60"
name="Adress60"
onChange={props.handleChange}
required
value={props.values.name}
variant="outlined"
/>
);
}
I really appreciate your help!

You could initialise state with same keys as name prop of textfields
const [values, setValues] = useState({
name1: '',
...
name60: '',
});
Set state in event handler as
const handleChange = (e) => {
setValues({
...values,
[e.target.name]: e.target.value
})
}
<TextField
fullWidth
label="Name"
name="name1"
onChange={props.handleChange}
required
value={props.values.name1}
variant="outlined"
/>
.
.
.
.

Related

React JS Typescript input onChange and onFocus error

I want to implement the following repo available on Github using ReactJS Typescript. I created 3 different methods as onChange, changeHandler, focusHandler but they are not working correctly. When I click on the Card Number field, I get a 'number.replace' error.
I need to fix onChange and onFocus methods in TextField fields.
https://github.com/amaroteam/react-credit-cards
import React, {useState, FocusEvent, ChangeEvent} from 'react'
import Cards from 'react-credit-cards';
import 'react-credit-cards/es/styles-compiled.css'
const PaymentMethodEditor = () => {
const [stateList, setState] = React.useState(initialValues)
const handleFormSubmit = async (values: any) => {
console.log(values)
}
const focusHandler = (event: React.FocusEvent<HTMLInputElement>) => {
setState({ focus: event.target.name })
}
const changeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
setState({ [name]: event.target.value })
}
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.currentTarget.value;
setState({ [name]: event.target.value })
}
return (
<CustomerDashboardLayout>
<DashboardPageHeader
icon={CreditCard}
title={`${id === 'add' ? 'Add New' : 'Edit'} Payment Method`}
button={
<Link href="/payment-methods">
<Button color="primary" sx={{ bgcolor: 'primary.light', px: '2rem' }}>
Back to Payment Methods
</Button>
</Link>
}
/>
<div id="PaymentForm">
<Cards
cvc={stateList.cvc}
expiry={stateList.exp}
focused={stateList.focus}
name={stateList.name}
number={stateList.card_no}
/>
<Card1>
<Formik
initialValues={initialValues}
validationSchema={checkoutSchema}
onSubmit={handleFormSubmit}
>
{({ values, errors, touched, handleChange, handleBlur, handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Box mb={4}>
<Grid container spacing={3}>
<Grid item md={6} xs={12}>
<TextField
name="card_name"
label="Card Name"
fullWidth
onBlur={handleBlur}
onChange={changeHandler}
onFocus={focusHandler}
value={values.card_name || ''}
error={!!touched.card_name && !!errors.card_name}
helperText={touched.card_name && errors.card_name}
/>
</Grid>
<Grid item md={6} xs={12}>
<TextField
name="card_no"
label="Card Number"
fullWidth
onBlur={handleBlur}
onChange={changeHandler}
onFocus={focusHandler}
value={values.card_no || ''}
error={!!touched.card_no && !!errors.card_no}
helperText={touched.card_no && errors.card_no}
/>
</Grid>
<Grid item md={6} xs={12}>
<TextField
name="name"
label="Name on Card"
fullWidth
onBlur={handleBlur}
onChange={handleChange}
value={values.name || ''}
error={!!touched.name && !!errors.name}
helperText={touched.name && errors.name}
/>
</Grid>
<Grid item md={6} xs={12}>
<TextField
name="exp"
label="Exp. Date"
fullWidth
onBlur={handleBlur}
onChange={handleChange}
value={values.exp || ''}
error={!!touched.exp && !!errors.exp}
helperText={touched.exp && errors.exp}
/>
</Grid>
<Grid item md={6} xs={12}>
<TextField
name="cvc"
label="CVC"
fullWidth
onBlur={handleBlur}
onChange={handleChange}
value={values.cvc || ''}
error={!!touched.cvc && !!errors.cvc}
helperText={touched.cvc && errors.cvc}
/>
</Grid>
</Grid>
</Box>
<Button type="submit" variant="contained" color="primary">
Save Changes
</Button>
</form>
)}
</Formik>
</Card1>
</div>
</CustomerDashboardLayout>
)
}
const initialValues = {
card_no: '',
name: '',
exp: '',
cvc: '',
focus: '',
number: '',
card_name: '',
}
export default PaymentMethodEditor
enter image description here
enter image description here

useFormik React - Update initialValues

I am using useFormik hook for my data handling. Adding the data is fine, but when I want to update the data in the same component I need to change the initial values to the custom object instead of having empty strings. Is there formik way of doing that with useFormik Hook? although I can achieve that with states, but then what will be the benefit of using the formik all along.
Kindly help!
const formik = useFormik({
initialValues: {
name: '',
post: '',
contact: '',
email: '',
password: '',
},
onSubmit: values => {
//handling submit
},
});
Form:
<form className={classes.form} onSubmit={formik.handleSubmit}>
<Grid container spacing={2}>
<Grid item xs={12} sm={6}>
<TextField
autoComplete="name"
variant="outlined"
required
fullWidth
label="Name"
autoFocus
id="name"
name="name"
onChange={formik.handleChange}
value={formik.values.name}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
variant="outlined"
required
fullWidth
label="Post"
autoComplete="post"
id="post"
name="post"
onChange={formik.handleChange}
value={formik.values.post}
/>
</Grid>
//Submit button and form close etc
Thanks
I found the solution that is:
For One field
const editEmployee = ()=> {
formik.setFieldValue("name","Imran");
alert("changed")
}
For Multiple:
formik.setValues({"name":"hey","post":"hoii"});
or send the whole Object

How i can pass existing data from my store to formik?

I have a hook component that uses useFormik() and this is my initial values:
const formik = useFormik({
initialValues: {login: '', password: '', inactive: false},
validationSchema: schema,
onSubmit: async (values, { resetForm }) => {
await dispatch(UserActions.createUser(values, resetForm))
},
})
Sometimes this component already have data, so i try to check this in useEffect and set the initialState by then:
const userEditing = useSelector(store => store.userStates.userEditing)
const [initialValues, setInitialValues] = useState('')
useEffect(() => {
if (userEditing) {
setInitialValues(userEditing)
} else {
setInitialValues({login: '', password: '', inactive: false})
}
}, []); // eslint-disable-line
and then i try to put this variable in initialValues:
const formik = useFormik({
initialValues: initialValues,
validationSchema: schema,
onSubmit: async (values, { resetForm }) => {
await dispatch(UserActions.createUser(values, resetForm))
},
})
Is entering in the if() condition but my inputs is not being filled with this values.
How i can pass existing data from my store to formik?
This is my template:
<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>
Pass enableReinitialize={true} to Formik wrapper.

How disable the auto reset form on submit in formik?

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

React Formik Material UI Autocomplete: How can I populate value inside of autocomplete from localStorage?

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

Resources