export const data = [
{
size: "S",
colorMap: { Yellow: 10, Green: 5, Black: 50 },
productName: "Shirt",
price: 200
}
];
I wanted to show the initial values of the colorMapand then update its quantity. How can I update the quantities of the colors which are the values of the Object.entries(colorMap)?
Codesandbox: https://codesandbox.io/s/form-changehanlder-2-2repsp?file=/part2.js
The product here came from the parent component:
This is the child component
import React, { useState } from "react";
import { Grid, TextField } from "#mui/material";
const Part2 = ({ product }) => {
const [qty, setQty] = useState();
const handleSubmit = (e) => {
e.preventDefault();
console.log(qty);
};
return (
<div>
{product &&
product.map((prod, index) => (
<>
<Grid item key={index}>
<form onSubmit={handleSubmit}>
{Object.entries(prod.colorMap).map((color, index) => (
<Grid
container
rowSpacing={1}
columnSpacing={{ xs: 1, sm: 2, md: 3 }}
key={color[0]}
>
<Grid item xs={6}>
<TextField
type="text"
variant="outlined"
label="Color"
fullWidth
value={color[0]}
disabled
/>
</Grid>
<Grid item xs={6}>
<TextField
type="number"
variant="outlined"
fullWidth
label="Quantity"
value={color[1]}
onChange={(e) => console.log(index)}
/>
</Grid>
</Grid>
))}
</form>
</Grid>
</>
))}
</div>
);
};
export default Part2;
First make changeHandler in demo.js as you are using React State in demo.js so you have to make onChangeHandler in that file and pass it in props of part2.
Like:
const onChangeValues = (propertyName, index, value) => {
let item = product?.[index];
if (item) {
item.colorMap[propertyName] = value;
let prods = [...product];
prods[index] = item;
setProduct(prods);
}
};
And pass this function in props of Part2:
<Grid item>
<Part2 product={product} onChange={onChangeValues} />
</Grid>
In part2 Component you can consume it as follows:
<TextField
type="number"
variant="outlined"
fullWidth
label="Quantity"
value={color[1]}
onChange={({ target: { value } }) => {
onChangeHandler(color[0], index, value);
}}
/>
Codesandbox Link: https://codesandbox.io/s/form-changehanlder-2-forked-l9unl0?file=/part2.js:1131-1521
Related
I'm from Angular and new to React. Im doing well but here is a problem I'm stuck at. As you can see I have BasicLayout and AppointmentForm, both are in one file. BasicLayout is being used inside AppointmentForm but not like an element i.e <BasicLayout/> so I'm not able to understand how to pass props or its even possible now. I want to trigger commitChanges(inside AppointmentForm) function when onSubmit(inside Basic Layout) function is triggered. How can I pass props between these components?
const BasicLayout = (props) => {
const formik = useFormik({
initialValues: {
title: '',
agenda: '',
description: '',
participants: [],
host: user?.id,
guest: '',
location: '',
},
validationSchema,
onSubmit: async (values) => {
values.startDate = props.appointmentData.startDate;
values.endDate = props.appointmentData.endDate;
values.guest = values.guest?._id;
createAppointment(values);
console.log(values);
},
});
return (
<Container>
<Typography sx={{ fontSize: 24, fontWeight: 'bold' }} color="text.primary" gutterBottom>
Create Appointment
</Typography>
<Box sx={{ flexGrow: 1 }}>
<FormikProvider value={formik}>
<Form autoComplete="off" onSubmit={handleSubmit}>
<Grid container spacing={2}>
<Grid item xs={6} md={6}>
<TextField
label="Title"
color="secondary"
id="title"
type="text"
key="title"
value={formik.values.title}
onChange={formik.handleChange}
{...getFieldProps('title')}
error={Boolean(touched.title && errors.title)}
helperText={touched.title && errors.title}
fullWidth
/>
</Grid>
<Grid item container xs={12} md={12} direction="row" justifyContent="center" alignItems="center">
<LoadingButton size="medium" type="submit" variant="contained" loading={isSubmitting}>
Create
</LoadingButton>
</Grid>
</Grid>
</Form>
</FormikProvider>
</Box>
<ToastContainer />
</Container>
);
};
const AppointmentsDashboard = (props) => {
const commitChanges = ({ added, changed, deleted }) => {
console.log(props);
console.log({ added, changed, deleted });
if (added) {
if (!isValidate) {
notify('Please fill all required fields', 'error');
return;
}
const startingAddedId = data.length > 0 ? data[data.length - 1].id + 1 : 0;
setData([...data, { id: startingAddedId, ...added }]);
}
if (changed) {
setData(
data.map((appointment) =>
changed[appointment.id] ? { ...appointment, ...changed[appointment.id] } : appointment
)
);
}
if (deleted !== undefined) {
setData(data.filter((appointment) => appointment.id !== deleted));
}
return data;
};
return (
<>
<Paper>
<Scheduler data={data} height={660}>
<ViewState currentDate={currentDate} />
<EditingState
onCommitChanges={commitChanges}
addedAppointment={addedAppointment}
onAddedAppointmentChange={changeAddedAppointment}
appointmentChanges={appointmentChanges}
onAppointmentChangesChange={changeAppointmentChanges}
editingAppointment={editingAppointment}
onEditingAppointmentChange={changeEditingAppointment}
onAppointmentFormClosing={() => {
console.log('asdasd');
}}
allowAdding={true}
/>
<WeekView startDayHour={9} endDayHour={17} />
<AllDayPanel />
<EditRecurrenceMenu />
<ConfirmationDialog />
<Appointments />
<AppointmentTooltip showOpenButton showDeleteButton />
<AppointmentForm basicLayoutComponent={BasicLayout} />
</Scheduler>
</Paper>
</>
);
};
export default AppointmentsDashboard;
I am trying to use a more concise method of declaring the initial state of my form values by declaring them in an object within useState and having them be updated by user input.
However I get the error "Uncaught TypeError: setFieldValue.providerName is not a function".
Is my approach completely wrong? or is there a small detail I am missing?
code below
import { Container, FormControl, FormControlLabel, FormLabel, Grid, InputLabel, Paper, Radio, RadioGroup, TextField } from '#mui/material';
import Button from '#mui/material/Button';
import { useCallback, useContext, useEffect, useState } from 'react';
import { AppContext } from '../../context/context';
import DatePicker from '../../elements/form/DatePicker';
import MultipleSelect from '../../elements/form/Select';
import MultipleSelectChip from '../../elements/form/SelectMulti';
import MuiTextField from '../../elements/form/TextField';
const ProjectForm = () => {
const [fieldValue, setFieldValue] = useState({
projectName: "",
providerName: "",
clientName: "",
description: "",
budget: 0,
})
const [activeButtons, setActiveButtons] = useState({
maconomyMavenlink: false,
maconomy: false,
mavenlink: false
})
const [formActive, setFormActive] = useState(false)
const { mavenlinkConnected } = useContext(AppContext);
const { maconomyConnected } = useContext(AppContext);
const checkFormActive = () => {
if(maconomyConnected || mavenlinkConnected){
setFormActive(true);
}
}
const checkButtonsActive = useCallback(() => {
if (mavenlinkConnected){
setActiveButtons({
mavenlink: true
})
}
if (maconomyConnected){
setActiveButtons({
maconomy: true
})
}
if (mavenlinkConnected && maconomyConnected){
setActiveButtons({
maconomyMavenlink: true
})
}
}, [])
useEffect(() => {
checkButtonsActive();
},[])
useEffect(() => {
checkFormActive()
}, [])
const handleSubmit = () => {
console.log("form submitted")
}
const handleResetForm = () => {
setFieldValue.projectName("");
setFieldValue.providerName("");
setFieldValue.clientName("");
setFieldValue.description("");
setFieldValue.projectName("");
setFieldValue.budget(0);
}
//Dummy values for time being
const value = "value";
const handleChange = () => {
}
const templates = [
'template 1',
'template 2',
'template 3',
'template 4',
'template 5',
'template 6'
]
const groups = [
'group 1',
'group 2',
'group 3',
'group 4',
'group 5',
'group 6',
]
const currencies = [
'British Pound (£)',
'United States Dollar ($)'
]
const taskBilling = [
'Fixed Fee',
'Time & Materials'
]
const taskBillingModes = [
'Billable',
'Non-billable'
]
return(
<Container>
{formActive ?
<form onSubmit={handleSubmit} className="project-form-wrapper">
<Grid container spacing={1}>
<Grid item xs={12}>
<div className="button-container">
<Button disabled={!activeButtons.maconomyMavenlink} variant="contained">Mavenlink+Maconomy</Button>
<Button disabled={!activeButtons.mavenlink} variant="contained">Mavenlink</Button>
<Button disabled={!activeButtons.maconomy} variant="contained">Maconomy</Button>
</div>
</Grid>
<Paper className="project-form">
<Grid container>
<Grid item xs={12}>
<FormControl>
<FormLabel id="provider-client">I am the</FormLabel>
<RadioGroup
row
aria-labelledby="controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={value}
onChange={handleChange}
>
<FormControlLabel value="provider" control={<Radio />} label="Provider" />
<FormControlLabel value="client" control={<Radio />} label="Client" />
</RadioGroup>
</FormControl>
</Grid>
<Grid item xs={12}>
<TextField
fullWidth
onChange={e => setFieldValue.projectName(e.target.value)}
id="project-name"
label="Project Name"
variant="outlined"
value={fieldValue.projectName}
error={fieldValue.projectName === "" }
helperText={fieldValue.projectName === "" ? "Required" : " "}
/>
</Grid>
<Grid item xs={6}>
<MuiTextField
onChange={e => setFieldValue.providerName(e.target.value)}
name="provider-name"
id="provider-name"
label="Provider Name"
width="99%"
value={fieldValue.providerName}
/>
</Grid>
<Grid item xs={6}>
<MuiTextField
onChange={e => setFieldValue.clientName(e.target.value)}
error
name="client-name"
id="client-name"
label="Client Name"
width="100%"
value={fieldValue.clientName}
/>
</Grid>
<Grid item xs={12}>
<MultipleSelect
options={templates}
label="Template"
width="100%"
/>
<InputLabel>If selected budget, currency, task billing mode will be auto configured from selected template</InputLabel>
</Grid>
<Grid item xs={12}>
<MultipleSelectChip
options={groups}
/>
<InputLabel>Assign to one or more groups</InputLabel>
</Grid>
<Grid item xs={6}>
<DatePicker
label="Start Date"
/>
</Grid>
<Grid item xs={6}>
<DatePicker
label="End Date"
/>
</Grid>
<Grid item xs={12}>
<TextField
fullWidth
onChange={e => setFieldValue.description(e.target.value)}
id="description"
label="Description"
variant="outlined"
value={fieldValue.description}
/>
</Grid>
<Grid item xs={6}>
<MuiTextField
onChange={e => setFieldValue.budget(e.target.value)}
name="budget"
id="budget"
label="Budget"
width="99%"
variant="outlined"
value={fieldValue.budget}
error={fieldValue.budget === "" }
helperText={fieldValue.budget === "" ? "Required" : " "}
/>
</Grid>
<Grid item xs={6}>
<MultipleSelect
options={currencies}
label="Currency"
width="100%"
/>
</Grid>
<Grid item xs={6}>
<MultipleSelect
options={taskBilling}
label="Task Billing Mode"
width="99%"
/>
</Grid>
<Grid item xs={6}>
<MultipleSelect
options={taskBillingModes}
width="100%"
label="Task Mode"
/>
</Grid>
<Grid item xs={6}>
<Button onClick={handleResetForm}>Reset</Button>
<Button type="submit">Submit</Button>
</Grid>
</Grid>
</Paper>
</Grid>
</form>
: <h1>Please connect to Maconomy or Mavenlink to use this feature</h1>}
</Container>
)
}
export default ProjectForm;
const handleResetForm = () => {
setFieldValue.projectName("");
setFieldValue.providerName("");
setFieldValue.clientName("");
setFieldValue.description("");
setFieldValue.projectName("");
setFieldValue.budget(0);
}
is wrong. use
setFieldValue({ projectName: "",
providerName: "",
clientName: "",
description: "",
budget: 0})
I have this array where it has a color and a quantity. And when saving in Firestore, it saves as a string. How can I save the colorStocks in Firestore as a number?
This is how I save this in Firestore:
const [productName, setProductName] = useState();
const [size, setSize] = useState();
const [price, setPrice] = useState();
const [colorList, setColorList] = useState([{ color: "", colorStocks: "" }]);
const handleColorChange = (e, index) => {
const { name, value } = e.target;
const list = [...colorList];
list[index] = { ...list[index] }; // copy the item too
list[index][name] = value;
setColorList(list);
};
const handleSubmit = async (e) => {
e.preventDefault();
const docRef = await addDoc(collection(db, "products"), {
prodName: productName,
size: size + "-" + size1,
colorList,
});
console.log("Document written with ID: ", docRef.id);
};
The colors text fields are dynamic, hence, I used an array:
<form onSubmit={handleSubmit}>
<Grid>
<Grid item xs>
//product textfield here
</Grid>
<Grid item xs>
//some select here
</Grid>
<Grid item xs>
size
/>
</Grid>
</Grid>
<Typography variant="h6">Colors</Typography>
{colorList.map((singleColor, index) => (
<div key={index}>
<div>
<Grid>
<TextField
label="color"
name="color"
type="text"
id="color"
required
value={singleColor.color}
onChange={(e) => handleColorChange(e, index)}
/>
</Grid>
<br />
<Grid>
<TextField
label="Stocks"
name="colorStocks"
type="text"
id="colorStocks"
required
value={singleColor.colorStocks}
onChange={(e) => handleColorChange(e, index)}
/>
</Grid>
</div>
<br />
<Grid item xs>
//button to remove color
</Grid>
<br />
//button to add color
</div>
))}
<br />
<Divider />
<br />
<Grid item xs>
<Button type="submit">Submit</Button>
</Grid>
</form>
Is there way where I could destructure colorList to save it as a Number() in firestore? Or what are the other ways this would be solved? This should be as a number since this would be subtracted later on.
The colorList in console:
Just convert colorStocks to number before sending to Firestore.
const handleColorChange = (e, index) => {
const { name, value } = e.target;
setColorList(prevState => {
let list = [...prevState];
list[index][name] = name === 'colorStocks' ? +value : value;
return list;
});
};
I am making an add sales component that requires an add button to add multiple products. I made it working with normal TextField with following code but when added Autocomplete, I am unable. Looking for help.
import React, { useState } from "react";
import {
Box,
TextField,
Grid,
InputLabel,
Tooltip,
IconButton,
Autocomplete
} from "#mui/material";
import { Add, Remove } from "#mui/icons-material";
const products = [
{ id: 1, name: "Green Apple" },
{ id: 2, name: "Red Cherry" },
{ id: 3, name: "Strawberry" },
{ id: 4, name: "Ground Apple" },
{ id: 5, name: "Dragon Fruit" },
{ id: 6, name: "White pear" }
];
function AddSale() {
const [inputList, setInputList] = useState([{ name: "", qty: "", rate: "" }]);
// handle input change
const handleInputChange = (e, index) => {
const { name, value } = e.target;
const list = [...inputList];
list[index][name] = value;
setInputList(list);
};
// handle click event of the Remove button
const handleRemoveClick = (index) => {
const list = [...inputList];
list.splice(index, 1);
setInputList(list);
};
// handle click event of the Add button
const handleAddClick = () => {
setInputList([...inputList, { name: "", qty: "", rate: "" }]);
};
return (
<Grid container spacing={2}>
<Grid item sx={{ py: 0 }} xs={5}>
<InputLabel>Select a item*</InputLabel>
</Grid>
<Grid item sx={{ py: 0 }} xs={2}>
<InputLabel>Qty (Piece)*</InputLabel>
</Grid>
<Grid item sx={{ py: 0 }} xs={3}>
<InputLabel>Rate (Rs)*</InputLabel>
</Grid>
<Grid item sx={{ py: 0 }} xs={2} />
{inputList.map((x, i) => {
return (
<React.Fragment key={i}>
<Grid item xs={5}>
<Autocomplete
value={x.name}
onChange={(e) => handleInputChange(e, i)}
inputValue={x.name}
onInputChange={(e) => handleInputChange(e, i)}
id="select-product"
options={products}
disableClearable
getOptionLabel={(option) => option.name}
renderOption={(props, option) => (
<li {...props}>{option.name}</li>
)}
renderInput={(params) => (
<TextField
name="name"
{...params}
placeholder="Select product"
/>
)}
/>
</Grid>
<Grid item xs={2}>
<Box>
<TextField
id="sale-qty"
name="qty"
value={x.qty}
onChange={(e) => handleInputChange(e, i)}
variant="outlined"
fullWidth
/>
</Box>
</Grid>
<Grid item xs={3}>
<Box>
<TextField
id="sale-rate"
name="rate"
value={x.rate}
onChange={(e) => handleInputChange(e, i)}
variant="outlined"
fullWidth
/>
</Box>
</Grid>
<Grid item xs={2} sx={{ display: "flex" }}>
{inputList.length !== 1 && (
<Tooltip title="Remove">
<IconButton
onClick={() => handleRemoveClick(i)}
color="error"
>
<Remove color="error" />
</IconButton>
</Tooltip>
)}
{inputList.length - 1 === i && (
<Tooltip title="Add new">
<IconButton onClick={handleAddClick}>
<Add />
</IconButton>
</Tooltip>
)}
</Grid>
</React.Fragment>
);
})}
</Grid>
);
}
export default AddSale;
Here is screenshot how it should look like
Also codesandbox of current state
https://codesandbox.io/s/ecstatic-sunset-hxrfy?file=/src/App.js:0-3803
I have a problem fetching the shipping options, the error: 'Unhandled Rejection (TypeError): undefined is not an object (evaluating 'options[0].id')'. I have checked everything I can think of to debug this issue. I am using check.io as for my e-commerce backend. I have added different shipping option on that site however they are not showing up. I have a hard time understanding why there is a type error
import React, { useState, useEffect } from "react";
import {
InputLabel,
Select,
MenuItem,
Button,
Grid,
Typography,
} from "#material-ui/core";
import { useForm, FormProvider } from "react-hook-form";
import { Link } from "react-router-dom";
import FormInput from "../CheckoutForm/CustomTxtField";
import { commerce } from "../../lib/Commerce";
const AddressForm = ({ checkoutToken, test }) => {
const [shippingCountries, setShippingCountries] = useState([]);
const [shippingCountry, setShippingCountry] = useState("");
const [shippingSubdivisions, setShippingSubdivisions] = useState([]);
const [shippingSubdivision, setShippingSubdivision] = useState("");
const [shippingOptions, setShippingOptions] = useState([]);
const [shippingOption, setShippingOption] = useState("");
const methods = useForm();
const fetchShippingCountries = async (checkoutTokenId) => {
const { countries } = await commerce.services.localeListShippingCountries(
checkoutTokenId
);
setShippingCountries(countries);
setShippingCountry(Object.keys(countries)[0]);
};
const fetchSubdivisions = async (countryCode) => {
const { subdivisions } = await commerce.services.localeListSubdivisions(
countryCode
);
setShippingSubdivisions(subdivisions);
setShippingSubdivision(Object.keys(subdivisions)[0]);
};
const fetchShippingOptions = async (
checkoutTokenId,
country,
stateProvince = null
) => {
const options = await commerce.checkout.getShippingOptions(
checkoutTokenId,
{ country, region: stateProvince }
);
setShippingOptions(options);
setShippingOptions(options[0].id);
};
useEffect(() => {
if (checkoutToken && checkoutToken.id) {
fetchShippingCountries(checkoutToken.id);
console.log(checkoutToken.id);
}
}, [checkoutToken]);
useEffect(() => {
if (shippingCountry) fetchSubdivisions(shippingCountry);
}, [shippingCountry]);
useEffect(() => {
if (shippingSubdivision)
fetchShippingOptions(
checkoutToken.id,
shippingCountry,
shippingSubdivision
);
}, [shippingSubdivision]);
return (
<div>
<Typography variant="h6" gutterBottom>
Shipping address
</Typography>
<FormProvider {...methods}>
<form
onSubmit={methods.handleSubmit((data) =>
test({
...data,
shippingCountry,
shippingSubdivision,
shippingOption,
})
)}
>
<Grid container spacing={3}>
<FormInput name="firstName" label="First name" />
<FormInput name="lastName" label="Last name" />
<FormInput name="address1" label="Address line 1" />
<FormInput name="email" label="Email" />
<FormInput name="city" label="City" />
<FormInput name="zip" label="Zip / Postal code" />
<Grid item xs={12} sm={6}>
<InputLabel>Shipping Country</InputLabel>
<Select
value={shippingCountry}
fullWidth
onChange={(e) => setShippingCountry(e.target.value)}
>
{Object.entries(shippingCountries)
.map(([code, name]) => ({ id: code, label: name }))
.map((item) => (
<MenuItem key={item.id} value={item.id}>
{item.label}
</MenuItem>
))}
</Select>
</Grid>
<Grid item xs={12} sm={6}>
<InputLabel>Shipping Subdivision</InputLabel>
<Select
value={shippingSubdivision}
fullWidth
onChange={(e) => setShippingSubdivision(e.target.value)}
>
{Object.entries(shippingSubdivisions)
.map(([code, name]) => ({ id: code, label: name }))
.map((item) => (
<MenuItem key={item.id} value={item.id}>
{item.label}
</MenuItem>
))}
</Select>
</Grid>
<Grid item xs={12} sm={6}>
<InputLabel>Shipping Options</InputLabel>
<Select
value={shippingOption}
fullWidth
onChange={(e) => setShippingOption(e.target.value)}
>
{shippingOptions
.map((sO) => ({
id: sO.id,
label: `${sO.description} - (${sO.price.formatted_with_symbol})`,
}))
.map((item) => (
<MenuItem key={item.id} value={item.id}>
{item.label}
</MenuItem>
))}
</Select>
</Grid>
</Grid>
<br />
<div style={{ display: "flex", justifyContent: "space-between" }}>
<Button component={Link} variant="outlined" to="/cart">
Back to Cart
</Button>
<Button type="submit" variant="contained" color="primary">
Next
</Button>
</div>
</form>
</FormProvider>
</div>
);
};
export default AddressForm;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
This was a commercejs.com issue not a coding issue