In my code, I'm trying to set a value to the particular state variables using event method in REACT JS. and also make some calculation for a Discounted price.
After Calculation, set the updated value to the state. But it should be updated lately whenever the next event occurs.
this.state = {
isAddVariant: false,
vendors: [],
variant_price: '',
variant_priceDiscount: '',
variant_priceDiscounted: '',
variant_unit: '',
variants: [],
}
onVariantType = (e) => {
this.setState({
[e.target.name]: e.target.value,
})
if (e.target.name === 'variant_price' || e.target.name === 'variant_priceDiscount') {
let variant_priceDiscounted = this.state.variant_priceDiscount !== '' ? (this.state.variant_price - (this.state.variant_price * (this.state.variant_priceDiscount / 100))).toFixed(2) : this.state.variant_price
this.setState({ variant_priceDiscounted })
}
}
// render function
<Typography variant="h7" color="inherit" style={{ marginTop: 20 }}>
Add Product Variants
</Typography>
<Grid container spacing={24}>
{/* Input - Price */}
<Grid item xs={2}>
<TextField
type={'number'}
name={'variant_price'}
value={nullToEmptyString(variant_price)}
onChange={this.onVariantType}
label={'Price'}
placeholder={'Enter price in ₹'}
required={true}
margin={'dense'}
autoComplete={'off'}
fullWidth
/>
</Grid>
{/* Input - Unit */}
<Grid item xs={2}>
<TextField
name={'variant_unit'}
value={nullToEmptyString(variant_unit)}
onChange={this.onVariantType}
label={'Unit'}
placeholder={'Enter unit (eg: kg)'}
required={true}
margin={'dense'}
autoComplete={'off'}
fullWidth
/>
</Grid>
{/* Input - Discount */}
<Grid item xs={2}>
<TextField
type={'number'}
name={'variant_priceDiscount'}
value={nullToEmptyString(variant_priceDiscount)}
onChange={this.onVariantType}
label={'Discount'}
placeholder={'Enter discount in %'}
margin={'dense'}
autoComplete={'off'}
fullWidth
/>
</Grid>
<Grid item xs={4}>
<TextField
type={'number'}
name={'variant_priceDiscounted'}
value={nullToEmptyString(variant_priceDiscounted)}
label={'Discounted Price'}
margin={'dense'}
autoComplete={'off'}
disabled
fullWidth
/>
</Grid>
<Grid item xs={2}>
{/* Button - Add */}
<IconButton
type={'add'}
aria-label={'Add'}
color={'primary'}
onClick={this.onAddVariant}
>
<IconCheck />
</IconButton>
</Grid>
</Grid>
Expected behavior was Updated Discounted Value is calculated and displayed immediately.
Actual behaviour was the value lately updated Actual behavior image:
setState is async, and take time to update the state. setState takes a callback, you can use it,
onVariantType = (e) => {
let name = e.target.name; //store the name in a variable to use in callback
this.setState({
[e.target.name]: e.target.value,
}, () => {
if (name === 'variant_price' || name === 'variant_priceDiscount') {
let variant_priceDiscounted = this.state.variant_priceDiscount !== '' ? (this.state.variant_price - (this.state.variant_price * (this.state.variant_priceDiscount / 100))).toFixed(2) : this.state.variant_price
this.setState({ variant_priceDiscounted })
}
})
}
Note: We stored e.target.name in a name variable, because after seState execute's e gets nullifies. Read more about Synthetic events.
Related
I am creating a Poll with title and options. The title is stored using useRef and it works fine.
The options for the poll are generated uisng - state.pollOptions.map
export default function CreatePoll() {
const titleRef = useRef();
const [state, setState] = useState({
pollOptions: ['Yes', 'No'],
published: false,
});
...
const handleSubmit = async () => {
console.log(titleRef.current.value) // shows value entered in text field
...
<Box mb={2} mt={4} pt={4} px={3}>
<TextField
id="standard-multiline-flexible"
label="Poll Title"
name="pollTitle"
multiline
maxRows={4}
value={state.pollTitle}
inputRef={titleRef}
variant="standard"
fullWidth
/>
</Box>
<Box p={3}>
{state.pollOptions.map((option, index) => (
<Grid container spacing={3} mt={index==0?0:2}>
<Grid item xs={3}>
<TextField
size='small'
required
id={`value_field_${index}`}
label="Value"
defaultValue={index}
className="mb-1 mr-2"
fullWidth
/>
</Grid>
<Grid item xs={9}>
<TextField
required
size='small'
id={`value_field_${index}`}
label="Option Title"
defaultValue={option}
className="mb-1"
fullWidth
/>
</Grid>
</Grid>
))}
</Box>
The options for the poll are generated using an array. I am planning to add a button to add and remove options from array.
I am not sure how to track the values of these generated options in a state or a Ref.
I have multiple checkboxes in an edit modal form, which has a record structure for update, containing the inputs including the checkboxes in state.
There is one onChange event for the inputs.
When I click on a checkbox, the onChnage 'handleInputChanges' does execute.
The evt.target.checked is true or false.
The evt.target.name is correct and matches the name in the updateRecordStructure.
But the checkbox won't display the checked or unchecked status.
The markup:
<Grid item xs={5}>
<FormControlLabel variant="outlined" size="small"
control={<Checkbox
checked={defaultChecked}
color="primary"
name={name}
onChange={handleInputChanges}/>
}
label={label}
id={id}
/>
</Grid>
const updateRecordStructure ={
id: 0,
name: '',
canDo1b: false,
canDo1a: false,
canDo2b: false,
canDo2a: false
};
const [editRecordState, setEditRecordState] = React.useState(
updateRecordStructure
);
const handleInputChanges = (evt) => {
// Distinguish which input the change is coming from using the target.name.
// The square brackets around target.name, creates a dynamic key of that targets name in the object.
if (evt.target.name !== '') {
const value = evt.target.value;
if (evt.target.checked) {
setEditRecordState({
...editRecordState,
[evt.target.name]: evt.target.checked
});
} else {
setEditRecordState({
...editRecordState,
[evt.target.name]: value
});
}
}
};
Your state is not even connected to the check box.
Your code:
<Grid item xs={5}>
<FormControlLabel variant="outlined" size="small"
control={
<Checkbox
checked={defaultChecked} // You are supposed to specify your state here
color="primary"
name={name}
onChange={handleInputChanges}
/>
}
label={label}
id={id}
/>
</Grid>
i.e.
<Grid item xs={5}>
<FormControlLabel variant="outlined" size="small"
control={
<Checkbox
checked={editRecordState[name]}
color="primary"
name={name}
onChange={handleInputChanges}
/>
}
label={label}
id={id}
/>
</Grid>
If you wish to make certain checkboxes checked by default, update updateRecordStructure instead.
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
I need to run the address verification api call .During these scenarios
*when all associated fields are filled out.
*when above call is done , it should be calling when any of the fields value has
changed.
I tried triggering giving all the fields as dependencies in the useEffects second parameter array,but its calls the effect repeatedly
const Address = props => {
const { countries, usStates, caStates, title, binding, formik } = props;
var zip = formik.values.Client.Address.Residential.Zip;
var city = formik.values.Client.Address.Residential.City;
var line1 = formik.values.Client.Address.Residential.Address1;
var country = formik.values.Client.Address.Residential.Country;
var state = formik.values.Client.Address.Residential.State;
useEffect(() => {
if (zip && city && country && state && country) {
console.log("call address verification")
}
}, [zip, city, country, state, country])
return (
<TransactConsumer>
{({ userSave, getFormApi, formFunction, formStart }) => {
return (
<Fragment>
{title && <Grid item xs={12}>
<Typography variant="body1">{title}</Typography>
</Grid>}
<Grid item xs={12}>
<SectionField
title={title}
name={binding + ".Country"}
required
defaultValue={{ label: "United States", value: "US" }}
label="Country"
suggestions={countries}
component={MuiReactSelect}
/>
</Grid>
<Grid item xs={12}>
<SectionField
title={title}
name={binding + ".Address1"}
required
label="Address Line 1"
fullWidth
component={TextField}
/>
</Grid>
<Grid item xs={12}>
<SectionField
title={title}
name={binding + ".Address2"}
label="Address Line 2"
fullWidth
component={TextField}
/>
</Grid>
<Grid item xs={12} sm={6}>
<SectionField
title={title}
name={binding + ".City"}
required
label="City"
fullWidth
component={TextField}
/>
</Grid>
<Grid item xs={12} sm={4}>
<SectionField
title={title}
name={binding + ".State"}
required
label={isUsCountry() ? "State" : isCaCountry() ? "Province" : "State / Province"}
fullWidth
component={ MuiReactSelect}
/>
</Grid>
<Grid item xs={12} sm={2}>
<SectionField
title={title}
name={binding + ".Zip"}
required
label="Zip"
fullWidth
component={TextField}
/>
</Grid>
</Fragment >
)
}}
</TransactConsumer>
)
}
====SectionField====
import React, { useEffect } from 'react'
import useSectionData from './useSectionData';
import { Field } from 'formik';
import PropTypes from 'prop-types';
const SectionField = ({ children, title, name, ...rest }) => {
const { addField } = useSectionData();
useEffect(() => {
addField(title, name)
}, [title, name])
return (
<Field name={name} {...rest}>
{children}
</Field>
)
}
SectionField.propTypes = {
title: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node),
PropTypes.node]),
};
export default SectionField
Section Field component is wrapper for the formik Field Element.
what would be the best way to make sure I can call only after all the
fields have been filled out . Right now , the it gets called for every
click , like lets say zip is 60000 it calls useEffect 10 times
what can be an other option rather than using formik values to
as dependencies.Any best practices could be helpful. Thanks .
You can have a variable you keep in state that indicates whether all of the fields have been filled out or not. You'd set that variable in the current useEffect that you have. It'd look something like this:
const [ allFieldsFilled, setAllFieldsFilled ] = useState(false);
useEffect(() => {
setAllFieldsFilled(zip && city && country && state && country)
}, [zip, city, country, state, country])
Once you have an indication of whether the fields have all been filled out or not, you could have a second useEffect that'd be responsible for triggering the validation (you could maybe combine them into one, but I think separating them would make the intent a bit clearer):
useEffect(() => {
if(allFieldsFilled){
performValidation();
}
}, [zip, city, country, state, country])
To keep yourself from having to type all the fields you want to be triggering the validation, you could do something like this:
const validationFields = [zip, city, country, state];
useEffect(()=>{
//Code
}, [...validationFields])
how can we delete the formik values when using field arrays .So Its gets
deleted from the UI when I delete it , but stays in the formik values.
can I edit/modify formik values directly ?
I am new to react here. Thanks
{
Client:
Phone: [
{
PhoneNumber:"",
PhoneType:""
}
]
}
I am able to delete the occurrence from the state, but formik values still retains the values.
const Phone = ({ title, binding }) => {
const [phones, setPhones] = useState([])
return (
<Fragment>
<Grid item xs={12} md={6}>
<SectionField
title={title}
name={binding + `.Phone.PhoneNumber`}
required
label="Phone"
fullWidth
type="tel"
component={TextField}
/>
</Grid>
<Grid item xs={12} sm={3}>
<SectionField
title={title}
name={binding + `.Phone.PhoneType`}
required
defaultValue={{ label: "", value: "" }}
label="Phone Type"
suggestions={phoneTypes}
component={MuiReactSelect}
/>
</Grid>
<IconButton onClick={() => {
setPhones(currentValue => [...currentValue, {
id: generate(),
PhoneNumber: "",
PhoneType: ""
}])
}}>
<AddBoxIcon fontSize="large" />
</IconButton>
{phones.map((p, index) => (
<Fragment key={p.id}>
<Grid item xs={12} md={5}>
<SectionField
title={title}
name={binding + `.Phone[${index}].PhoneNumber`}
required
label="Phone"
fullWidth
type="tel"
component={TextField}
/>
<ErrorMessage name={`Client.Phone.${index}.PhoneNumber`} /><br />
</Grid>
<Grid item xs={12} sm={3}>
<SectionField
title={title}
name={binding + `.Phone[${index}].PhoneType`}
required
defaultValue={{ label: "", value: "" }}
label="Phone Type"
suggestions={phoneTypes}
component={MuiReactSelect}
/>
</Grid>
<IconButton onClick={() => {
setPhones(currentPhone =>
currentPhone.filter(x => x.id !== p.id))
}}>
<RemoveCircleIcon fontSize="large" />
</IconButton>
</Fragment>
))}
</Fragment>
)
};export default Phone;
Formik values:
======================================================
"Phone": {
"0": {
"PhoneNumber": "8578882942",
"PhoneType": "Home"
},
"PhoneNumber": "8578882942",
"PhoneType": "Home"
},
I got into same sort of inconvenience when i wanted to delete that element from Ui but its values in formik stays there.
What i did was use formik's setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void
So lets say you are using a button to remove that element from UI so i added an extra something to it like this to its onClick property :
//formik.setFieldValue({name of the element that you want to delete})
onClick = {()=>{
//whatever work you need to do for removing it from ui
//then
//formik.setFieldValue({name of the element that you want to delete})
formik.setFieldValue(index)
// here index was the name of variable that i used for that element
}}
Then in the formik's useFormik() hook i did something like this:
const formik = useFormik({
initialValues: {
//your initial values
},
onSubmit: (values) => {
// your onSubmit
},
setFieldValue: (field) => {
delete values.field;
},
});
Sorry i know that this answer is late but i hope it would help someone