React updating specific object of an array of objects - javascript

I am trying to change a specific item of an object , which is contained by an array of objects, yet I cannot seem to find my mistake:
This is my formset array which contains the objects :
const [formset,setFormset]=React.useState([
{
id: uuidv4(),
product:"",
price: 0,
quantity: 0,
productSubtotal: 0,
}
])
And this is how I am trying to change the product , for example:
const handleProduct = (e,id) => {
setFormset(
formset.map(item=>
(item.id !== id? item :
{
...item , product: e.target.value
})
)
)
}
EDIT: The return statement from the parent component:
return (
<div>
{formset.map((item)=>{
return(
<PieChartGroupFormPresenter
item={item}
key={item.id}
id={item.id}
handleProduct={handleProduct}
handleQuantity={handleQuantity}
purchaseList={purchaseList}
product={item.product}
quantity={item.quantity}/>
)
}
)
}
<button type="button" onClick={handleClick}>More</button>
</div>
)
The presenter component :
const PieChartGroupFormPresenter=({handleProduct,handleQuantity,product,quantity,item,purchaseList,id})=>{
return (
<div>
<FormControl>
<Select onChange={(id)=>handleProduct(id)} value={product}>
{Object.keys(purchaseList).map((item,index) =>
<MenuItem value={item} key={index}>{item}</MenuItem>
)}
</Select>
<TextField onChange={(id)=>handleQuantity(id)} value={quantity} />
<TextField value={item.price} disabled>{item.price}</TextField>
</FormControl>
</div>
)
}
I have tried multiple variations of this approach yet none of them seem to work. Thank you very much!

You passing event as id, should be
onChange={(event)=>handleProduct(event, id)}
Same mistake for other field
//<TextField onChange={(id)=>handleQuantity(id)} value={quantity} />
<TextField onChange={(event)=>handleQuantity(event, id)} value={quantity} />

const handleProduct = (e) => {
setFormset({...formset,product: e.target.value})
}

Related

React.js MaterialUI: How to bind the value of a materiaUI select dropdown with the corrispective list item value?

I have a list of users and a Select dropdown from material UI with some values. I am able to console.log the values of the select but how can I know to which user in the List they refer to?
<List>
{userList.map((user:any) => (
<ListItem key={user.key}>
<ListItemAvatar>
<Avatar>
<PersonIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={user.name} />
<Select
value={userValue}
onChange={handleChange}
>
{dropdownvalues.map(
(g: { value: string}) => (
<MenuItem key={g.value} value={g.value}>
{g.value}
</MenuItem>
)
)}
</Select>
</ListItem>
))}
</List>
const handleChange=(e:any,index:any) => {
console.log(e.target.value)//here I am able to console log just the value how can I bind the user too given the fact that this funciton doesnt accept another parameter
}
Just add index as value as it is uniquely identifiable in the following code -
{dropdownvalues.map(
(g: { value: string},index:number) => (
<MenuItem key={g.value} value={index}>
{g.value}
</MenuItem>
)
)}
After that just access your selected user as -
const handleChange = (e:any) => {
const selectedInd = e.target.value;
console.log('index->',selectedInd);
console.log(dropdownvalues[e.target.value]);
}
Simplest way is to extend your handleChange function and call like this:
// ...
onChange={(evt) => handleChange(user)}
// ... And then extend the function:
const handleChange=(user:any) => {
console.log(user)
}

get field value from Formik & Material UI form

I am trying to disable a checkbox group based on the value of a radio group. I followed the method used in the last part of the Formik tutorial. Using react context removes a lot of clutter from the form itself but I'm not sure how to expose some values now.
In the form below, in the CheckboxGroup component, I'm attempting to print the word disabled as an attribute of checkbox1 if radio4's value is "yes". I'm not sure what value should be used here as fields doesn't work. How do I pass a value to the form given the React Context method used?
The form:
export default function HealthAssessmentForm() {
return (
<Formik
initialValues={{
radio4: '',
symptoms: '',
}}
onSubmit={async (values) => {
await new Promise((r) => setTimeout(r, 500));
console.log(JSON.stringify(values, null, 2));
}}
validator={() => ({})}
>
<Form>
<RadioInputGroup
label="Disable the checkbox?"
name="radio4"
options={['Yes','No']}
/>
<CheckboxGroup
{(fields.radio4.value === "yes") ? "disabled" : null}
name="checkbox1"
options={[
{name:"hello",label:"hello"},
{name:"there",label:"there"},
]}
/>
<button type="submit">Submit</button>
</Form>
</Formik>
)
}
I'm not sure the custom components are relevant here but...
const RadioInputGroup = (props) => {
const [field, meta] = useField({...props, type:'radio'});
return (
<FormControl component="fieldset">
<FormLabel component="legend">{props.label}</FormLabel>
<RadioGroup aria-label={props.name} name={props.name} value={props.value}>
<FieldArray name="options">
{({ insert, remove, push }) => (
props.options.length > 0 && props.options.map((option,index) => (
<FormControlLabel key={index} {...props} value={option.toLowerCase()} control={<Radio />} label={option} />
))
)}
</FieldArray>
</RadioGroup>
</FormControl>
)
};
const CheckboxGroup = (props) => {
const [field, meta] = useField({...props, type: 'checkbox', });
return (
<FormControl component="fieldset">
<FormLabel component="legend">{props.label}</FormLabel>
<FormGroup>
<FieldArray name="options">
{({ insert, remove, push}) => (
props.options.length > 0 && props.options.map((option,index) => (
<FormControlLabel
{...field} {...props}
key={index}
control={<Checkbox />}
label={option.label}
/>
))
)}
</FieldArray>
</FormGroup>
<FormHelperText>Be careful</FormHelperText>
</FormControl>
)
}
I wrapped the whole <Form> in a function that passes props as an argument. I then get access to props.values.radio1. However, that has exposed that radio1 does not have a value even when it is clicked, which should be a separate issue.
{(props) => (
<Form>
<RadioInputGroup
label="Disable the checkbox?"
name="radio4"
options={['Yes','No']}
/>
<CheckboxGroup
disabled={props.values.radio1 === "No"}
name="checkbox1"
options={[
{name:"hello",label:"hello"},
{name:"there",label:"there"},
]}
/> </Form>
)}

Why onChnage is not a function when a functionalities is migrated to a component for better readability

I'm trying to migrate a specific functionality to a component so I can have better readability in my file.
The functionality is this
display: (filterList, onChange, index, column) => {
const optionValues = preparedSites.reduce((acc, val) => {
const exists = acc.find(
country => country.code === val[3].code,
);
if (!exists) acc.push(val[3]);
return acc;
}, []);
return (
<FormControl>
<InputLabel htmlFor="select-multiple-chip">
Country
</InputLabel>
<Select
value={filterList[index]}
renderValue={selected => selected.join(' ')}
onChange={event => {
onChange([event.target.value], index, column);
}}
>
{optionValues.map(item => (
<MenuItem key={item.code} value={item.code}>
<Flag siteCountry={item} />
<ListItemText primary={item.code} />
</MenuItem>
))}
</Select>
</FormControl>
);
},
What I want to get is that
display: (filterList, onChange, index, column) => <Compnent filterList={filterList} onChange={onChange} index={index} column={column} />
I tried to make it like this but getting onChange is not a function
const Component = (filterList, onChange, index, column) => {
const optionValues = preparedSites.reduce((acc, val) => {
const exists = acc.find(country => country.code === val[3].code);
if (!exists) acc.push(val[3]);
return acc;
}, []);
return (
<FormControl>
<InputLabel htmlFor="select-multiple-chip">Country</InputLabel>
<Select
value={filterList[index]}
renderValue={selected => selected.join(' ')}
onChange={event => {
onChange([event.target.value], index, column);
}}
>
{optionValues.map(item => (
<MenuItem key={item.code} value={item.code}>
<Flag siteCountry={item} />
<ListItemText primary={item.code} />
</MenuItem>
))}
</Select>
</FormControl>
);
};
I would like to understand what wrong.
I decided to answer my own question as was a simple issue.
Probably do tiring of last pre-Christmas urgency deliveries :(
I forgot to add {} in my component:
Before
const Component = (filterList, onChange, index, column) => {}
After
const Component = ({filterList, onChange, index, column}) => {}
Now all seems to work correctly.

Remove asterisk from only one field

I have a custom component that takes in values from an API and then displays them to a user, however within this component I give a 'required' flag to give an asterisk to the label, however I only want one field as seen below to have an asterisk not both as is currently happening.
<Grid item xs={12} sm={6}>
<SearchUsers
name="primaryOfficerId"
label="PO Responsible"
id="primaryOfficerId"
onSelect={change.bind(null, 'primaryOfficerId')}
error={touched.primaryOfficerId && Boolean(errors.primaryOfficerId)}
/>
</Grid>
<Grid item xs={12} sm={6}>
<SearchUsers
name="supportOfficerId"
label="Support Officer"
id="supportOfficerId"
onSelect={change.bind(null, 'supportOfficerId')}
/>
</Grid>
And now my custom component
const Search = (props) => {
const [data, setData] = useState([]);
const [select, setSelect] = useState(0);
const { type: TYPE, name: NAME, label: LABEL, onSelect, filter } = props;
const applyFilter = (data) => {
let result = data;
if (filter) {
result = filter(data);
}
return result;
};
useEffect(() => {
getLookupData(TYPE)
.then((response) => {
setData(response);
})
.catch((error) => {
if (error === HttpStatus.NOT_FOUND) {
setData([]);
} else {
throw error;
}
});
}, [TYPE]);
const options = applyFilter(data).map((item) => (
<MenuItem value={item.id} key={item.id}>
{item[NAME]}
</MenuItem>
));
const handleChange = (event) => {
setSelect(event.target.value);
onSelect && onSelect(event);
};
const { classes } = props;
return (
<FormControl required className={classes.formControl} id={NAME} error={props.error}>
<FormControlLabel control={<InputLabel htmlFor={NAME}>{LABEL}</InputLabel>} />
<Select
name={TYPE}
value={select}
onChange={handleChange}
disabled={props.disabled || options.length === 0}
input={<Input name={TYPE} id={NAME} />}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
{options}
</Select>
</FormControl>
);
};
Below you can see an image of my problem, both PO, and So responsible have an asterisk. I need only PO to have this asterisk but my component does not currently allow for individuals
Simply make your component customizable by passing it a required prop as boolean then in your component make it dynamic :
<FormControl required={props.required}>
// ...
</FormControl>
So now you can use it with an asterisk <SearchUsers required /> or without <SearchUsers required={false} />
Add additional prop required to your Search component and then use it as a prop value for FormControl:
<FormControl required={props.required} />

Trying to bind data to state with input field that's being mapped in React

Im trying to bind my state values to the input values using a onChange function
handleChange = event => {
this.setState({
[event.target.name]: event.target.value
});
};
but my Input fields are inside of a map and I don't know how to give the input field specific name and value attribute for each iteration so the onChange function will work
Current Code:
state = {
row0: "",
row1: "",
row2: "",
row3: "",
row4: ""
};
handleChange = event => {
this.setState({
[event.target.name]: event.target.value
});
};
handleSubmit = event => {
console.log(this.state);
};
render() {
const {
row,
editMode,
index,
selectedIdx,
startEditing,
stopEditing
} = this.props;
const rowLen = row.length;
return (
<React.Fragment>
{row.map((eachRow, i) => {
if (rowLen === i + 1) {
return (
<React.Fragment key={i}>
{editMode === true && index === selectedIdx ? (
<TableCell align="right">
<Input className="input_key" name={`row${i}`} />
<IconButton
onClick={this.handleSubmit}
className="ml-4"
aria-label="Filter list"
>
<i className="fas fa-check" />
</IconButton>
</TableCell>
) : (
<TableCell key={i} align="right">
{eachRow}
<IconButton
onClick={startEditing}
className="ml-4"
aria-label="Filter list"
>
<i className="fas fa-pen" />
</IconButton>
</TableCell>
)}
</React.Fragment>
);
} else {
return (
<React.Fragment key={i}>
{editMode === true && index === selectedIdx ? (
<TableCell>
<Input className="input_key" />
</TableCell>
) : (
<TableCell> {eachRow} </TableCell>
)}
</React.Fragment>
);
}
})}
</React.Fragment>
);
}
}
I tried template literal to possible store the input values in the state but got returned error but I believe this isn't working since state is object and i'm trying add a string to object.
<Input
className="input_key"
name={`row${i}`}
value={this.state + `${i}`}
onChange={this.handleChange}
/>
so tried creating a function to filter out the key to match it but I don't think that the right way about going about it.
const daState = Object.keys(this.state).filter(state => {
return state === `row${i}`;
});
I need to figure out how to get [event.target.name]: event.target.value so that data will bind to the state maybe there's a different approach to binding the values to the state anything will help, thank you.
Also any side note on improving syntax will be greatly appreciated
Have a look here
Reactjs setState() with a dynamic key name?
inputChangeHandler: function (event) {
var stateObject = function () {
returnObj = {};
returnObj[this.target.id] = this.target.value;
return returnObj;
}.bind(event)();
this.setState(stateObject);
}

Categories

Resources