Im using Material UI Checkbox to create a form that takes input and then in theory adds the new values or updates the values into a state object. This form is used for both editing a holiday or creating a new holiday.
I'm struggling with finding why my state isnt being updated upon clicking on a checkbox or typing in an input box. The checkbox wont change to checked/unchecked and the input wont remvoe the value when i use backspace to remove characters or enter new characters.
Dialog Box:
HolidayData:
{
"id": 1,
"ID": 1,
"HolidayName": "New Year's Day",
"HolidayDate": "05/20/2020",
"Branch": null,
"Hours": null,
"Web": true,
"Phone": true,
"CoOp": false,
"Active": true,
"Submitted": null,
"SubmittedBy": null,
"Published": "05/20/2020",
"PublishedBy": "John.Doe"
}
DialogBox Code:
const HolidayDialog = (props) => {
const [noticeModal, setNoticeModal] = React.useState(false);
const [selectedDate, setSelectedDate] = React.useState(new Date());
const [holidayData, setHolidayData] = React.useState(props.data);
useEffect(() => {
setHolidayData(props.data);
setNoticeModal(props.open)
});
const handleDateChange = (date) => {
setSelectedDate(date);
};
const handleClose = () => {
setNoticeModal(false);
};
const handleChange = (e) => {
const { name, checked } = e.target;
setHolidayData((prevState) => ({ ...prevState, [name]: checked }));
};
const updateValues = (e) => {
const { name, value } = e.target;
setHolidayData((prevState) => ({ ...prevState, [name]: value }));
};
return (
<Dialog
open={noticeModal}
TransitionComponent={Transition}
keepMounted
onClose={handleClose}
aria-labelledby="notice-modal-slide-title"
aria-describedby="notice-modal-slide-description"
>
<DialogTitle id="customized-dialog-title" onClose={handleClose}>
{holidayData.HolidayName ? holidayData.HolidayName : 'Create New Holiday'}
</DialogTitle>
<DialogContent dividers>
<form noValidate autoComplete="off">
<div className="row">
<div className="col">
<TextField required name="HolidayName" id="outlined-basic" label="Holiday Name" variant="outlined" onChange={updateValues} value={holidayData.HolidayName ? holidayData.HolidayName : ''}/>
</div>
<div className="col">
<TextField id="outlined-basic" label="Branch" variant="outlined" onChange={updateValues} value={holidayData.Branch ? holidayData.Branch : 'ALL'}/>
</div>
</div>
<div className="row mt-3">
<div className="col">
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<KeyboardDatePicker
disableToolbar
variant="inline"
format="MM/dd/yyyy"
margin="normal"
id="date-picker-inline"
label="Date picker inline"
value={selectedDate}
onChange={handleDateChange}
KeyboardButtonProps={{
'aria-label': 'change date',
}}
/>
</MuiPickersUtilsProvider>
</div>
<div className="col">
<TextField id="outlined-basic" label="Hours" variant="outlined" onChange={updateValues} value={holidayData.Hours ? holidayData.Hours : 'Closed'}/>
</div>
</div>
<div className="row mt-3">
<div className="col d-flex flex-column">
<FormControlLabel
control={
<Checkbox
checked={holidayData.Web ? holidayData.Web : false}
onChange={handleChange}
name="Web"
color="primary"
/>
}
label="Show on Web?"
/>
<FormControlLabel
control={
<Checkbox
checked={holidayData.CoOp ? holidayData.CoOp : false}
onChange={handleChange}
name="CoOp"
color="primary"
/>
}
label="CoOp Holiday?"
/>
</div>
<div className="col d-flex flex-column">
<FormControlLabel
control={
<Checkbox
checked={holidayData.Phone ? holidayData.Phone : false}
onChange={handleChange}
name="Phone"
color="primary"
/>
}
label="Use in IVR?"
/>
<FormControlLabel
control={
<Checkbox
checked={holidayData.Active ? holidayData.Active : false}
onChange={handleChange}
disabled
name="active"
color="primary"
/>
}
label="Active"
/>
</div>
</div>
</form>
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleClose} color="default">
Cancel
</Button>
<Button autoFocus onClick={handleClose} color="primary">
Create Holiday
</Button>
</DialogActions>
</Dialog>
)
}
When i check a box or try to edit an input the handleChange and updateValues are firing. I believe it may be a syntax issue, but i cant seem to find anything. When console.log'ing event.target.name i get the correct name: Web for example
EDIT: The issue appears to be with the value and checked being equal to {holidayData.Phone ? holidayData.Phone : false} or the sorts. However if i bring the values down to {holidayData.Phone} itll start to throw errors:
A component is changing an uncontrolled input of type checkbox to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa).
It'll now allow me to check a checkbox but only once and throws this error over and over again and im not sure why or how to correct this?
Most probably the useEffect is causing the issue. You're resetting the data each time the component gets updated. You don't need that if you're already doing that with useState.
Check my example here with only the textbox: https://codesandbox.io/s/fancy-shape-14r63?file=/src/App.js
If you uncomment the useEffect, it stops working.
Related
I am designing a form in react in which I have two categories of dropdown. one is named as "What include" and the other is "What is not include". Now I want that if the user to select any option from the menu of "What Include", then that option(item) will be disabled in the other drop-down named "What not Include".
Here is the code for handle change and UI:
const [personwhatIncludeItems, setPersonwhatIncludeItems] = React.useState(
[]
);
const [personnotIncludeItems, setPersonnotIncludeItems] = React.useState([]);
const handlenotIncludeItemsChange = (event) => {
const {
target: { value },
} = event;
setPersonnotIncludeItems(
// On autofill we get a stringified value.
typeof value === "string" ? value.split(",") : value
);
};
const handlewhatIncludeItemsChange = (event) => {
const {
target: { value },
} = event;
setPersonwhatIncludeItems(
// On autofill we get a stringified value.
typeof value === "string" ? value.split(",") : value
);
};
<div className="col-6 mt-2 mb-2">
<FormControl fullWidth>
<InputLabel id="multiple-include-label">
{" "}
Includes
</InputLabel>
<Select
labelId="whatInclude-multiple-checkbox-label"
id="whatInclude-multiple-checkbox"
multiple
value={personwhatIncludeItems}
onChange={handlewhatIncludeItemsChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.join(", ")}
MenuProps={MenuProps}
>
{whatIncludeArr.map((whatIncludeItems) => (
<MenuItem
key={whatIncludeItems}
value={whatIncludeItems}
>
<Checkbox
checked={
personwhatIncludeItems.indexOf(
whatIncludeItems
) > -1
}
/>
<ListItemText primary={whatIncludeItems} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
<div className="col-6 mt-2 mb-2">
<FormControl fullWidth>
<InputLabel id="multiple-not-include-label">
{" "}
Not Includes
</InputLabel>
<Select
labelId="whatnotInclude-multiple-checkbox-label"
id="whatnotInclude-multiple-checkbox"
multiple
value={personnotIncludeItems}
onChange={handlenotIncludeItemsChange}
input={<OutlinedInput label="Not Include" />}
renderValue={(selected) => selected.join(", ")}
MenuProps={MenuProps}
>
{WhatNotIncludeArr.map((notIncludeItems) => (
<MenuItem
key={notIncludeItems}
value={notIncludeItems}
>
<Checkbox
checked={
personnotIncludeItems.indexOf(notIncludeItems) >
-1
}
/>
<ListItemText primary={notIncludeItems} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
Not sure if I fully understand the logic, but if the goal is to disable the options if selected else where, perhaps consider to add conditional disabled for each mapped option (if the other selected state already has the value, disable the option), perhaps such as:
Minimized demo of below example: stackblitz
Although not tagged as it, the posted code looks like MUI so the example will use MUI components, but otherwise the approach could still work with other libraries provided that a disabled property is supported.
"What Include" select:
<div className="col-6 mt-2 mb-2">
<FormControl fullWidth>
<InputLabel id="multiple-include-label"> Includes</InputLabel>
<Select
labelId="whatInclude-multiple-checkbox-label"
id="whatInclude-multiple-checkbox"
multiple
value={personwhatIncludeItems}
onChange={handlewhatIncludeItemsChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.join(", ")}
MenuProps={MenuProps}
>
{whatIncludeArr.map((whatIncludeItems) => (
<MenuItem
key={whatIncludeItems}
value={whatIncludeItems}
// 👇 Added disable condition here
disabled={personnotIncludeItems.includes(whatIncludeItems)}
>
<Checkbox
checked={personwhatIncludeItems.indexOf(whatIncludeItems) > -1}
// 👇 Added disable condition here
disabled={personnotIncludeItems.includes(whatIncludeItems)}
/>
<ListItemText primary={whatIncludeItems} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
"What is not include" select:
<div className="col-6 mt-2 mb-2">
<FormControl fullWidth>
<InputLabel id="multiple-not-include-label"> Not Includes</InputLabel>
<Select
labelId="whatnotInclude-multiple-checkbox-label"
id="whatnotInclude-multiple-checkbox"
multiple
value={personnotIncludeItems}
onChange={handlenotIncludeItemsChange}
input={<OutlinedInput label="Not Include" />}
renderValue={(selected) => selected.join(", ")}
MenuProps={MenuProps}
>
{WhatNotIncludeArr.map((notIncludeItems) => (
<MenuItem
key={notIncludeItems}
value={notIncludeItems}
// 👇 Added disable condition here
disabled={personwhatIncludeItems.includes(notIncludeItems)}
>
<Checkbox
checked={personnotIncludeItems.indexOf(notIncludeItems) > -1}
// 👇 Added disable condition here
disabled={personwhatIncludeItems.includes(notIncludeItems)}
/>
<ListItemText primary={notIncludeItems} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
I don't understand what I'm doing wrong in here.
The scenario is that I am on the user profile details and I want to edit the information but one input must be disable at all the time.
When I'm loading the page the input it is actually disabled but as soon as I click the edit button it is becoming editable like the rest of the other input.
Any suggestion on how to make it works, please?
const [disabled, setDisabled] = useState(false);
const handleClickEditMember = () => {
Actions.enableMemberEdit();
setDisabled(!disabled);
};
<Card className={clsx(classes.root, className)}>
<CardHeader
action={
<Button
color="primary"
id="edit-member-button"
onClick={() => handleClickEditMember()}
size="small"
variant="contained"
>
{t('members.edit')}
</Button>
}
title={member.companyName}
/>
<Divider />
<CardContent className={classes.content}>
<CardHeader title={t('members.company_profile')} />
<Grid container spacing={3}>
<Grid item xs={6}>
<TextField
className={classes.textField}
fullWidth
id="companyName"
InputProps={{
readOnly: true,
}}
label={t('members.company_name')}
margin="dense"
name="companyName"
placeholder={t('members.company_name')}
value={member.companyName}
variant="outlined"
/>
</Grid>
<Grid item xs={3}>
<TextField
className={classes.textField}
fullWidth
id="Id"
InputProps={{
readOnly: true,
}}
disabled={!disabled}
label={t('members.id')}
margin="dense"
name="Id"
placeholder={t('members.id')}
value={member.id}
variant="outlined"
/>
</Grid>
</Grid>
You're updating the state everytime in the handleClickEditMember() by doing setDisabled(!disabled); which will eventually set to true as its false initially.
So you'll have to
Remove that piece of code and the state update won't be triggered again!
And For the ID input field just use disabled={disabled}
<TextField
className={classes.textField}
fullWidth
id="Id"
InputProps={{
readOnly: true,
}}
**disabled**
label={t('members.id')}
margin="dense"
name="Id"
placeholder={t('members.id')}
value={member.id}
variant="outlined"
/>
Just disable the textfield. Don't assign any value to disabled property
Just resolved by doing this:
InputProps={{
readOnly: memberId ? true : false,
}}
Is there something wrong with the TextField component from Material UI that I'm missing?
My handleChange simply is not getting invoked.
I can type in value in the inputs, but state of the component is not changing.
Here is the Auth component:
const Auth = () => {
const [formData, setFormData] = useState(initialFormState)
const handleSubmit = (e) => {
e.preventDefault();
console.log(formData);
}
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value
})
}
return (
<Container component="main" maxWidth="xs">
<Paper className={classes.paper} elevation={3}>
<form className={classes.form} onSubmit={handleSubmit}>
<Grid container spacing={2}>
<Input name="email" label="e-mail" handleChange={handleChange} type="email" />
<Input name="password" label="password" handleChange={handleChange} type={showPassword ? "text" : "password"} handleShowPassword={handleShowPassword} />
</Grid>
<Button type="submit" variant="contained" color="primary" className={classes.submit} fullWidth>
{isSignup ? "Sign up" : "Sign in"}
</Button>
</form>
</Paper>
</Container>
);
}
And the input component which is inside Auth:
const Input = ({ name, half, handleChange, type, label, autoFocus, handleShowPassword }) => {
return (
<>
<Grid item xs={12} sm={half ? 6 : 12}>
<TextField
name={name}
handleChange={handleChange}
variant="outlined"
required
fullWidth
label={label}
autoFocus={autoFocus}
type={type}
InputProps={name === "password" ? {
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={handleShowPassword}>
{type === "password" ? <Visibility/> : <VisibilityOff/>}
</IconButton>
</InputAdornment>
)
} : null}
/>
</Grid>
</>
);
}
You've to change the input attribute from handleChange to onChange
<Input name="email" label="e-mail" onChange={handleChange} type="email" />
<Input name="password" label="password" onChange={handleChange} type={showPassword ? "text" : "password"} handleShowPassword={handleShowPassword} />
I have the next application:
https://codesandbox.io/s/suspicious-murdock-87j13?file=/SubForm.js
<Form.List name="names">
{(fields, { add, remove }) => {
return (
<div>
{fields.map((field, index) => {
console.log("m", field);
return (
<Form.Item
{...(index === 0
? formItemLayout
: formItemLayoutWithOutLabel)}
label={index === 0 ? "Passengers" : ""}
required={false}
key={index}
>
<Form.Item
{...field}
name={[field.name, "outer"]}
fieldKey={[field.fieldKey, "outer"]}
validateTrigger={["onChange", "onBlur"]}
rules={[
{
required: true,
whitespace: true,
message:
"Please input passenger's name or delete this field."
}
]}
noStyle
>
<Input
placeholder="passenger name"
style={{ width: "60%" }}
/>
</Form.Item>
<Form.Item
{...field}
name={[field.name, "outer1"]}
fieldKey={[field.fieldKey, "outer1"]}
validateTrigger={["onChange", "onBlur"]}
rules={[
{
required: true,
whitespace: true,
message:
"Please input passenger's name or delete this field."
}
]}
noStyle
>
<Input
placeholder="passenger name"
style={{ width: "60%" }}
/>
</Form.Item>
<Form.Item>
<InnerForm fieldKey={field.key} />
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
style={{ margin: "0 8px" }}
onClick={() => {
remove(field.name);
}}
/>
) : null}
</Form.Item>
);
})}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
style={{ width: "60%" }}
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
When user click on Add field button, in console appears the warning:
Warning: Encountered two children with the same key, `0`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
Also this warning appears when i click on Add sub-field. I can't figure out why this warning appears.
Question: Why the above warning appears and how to remove it?
You have two Form.Item Component in your code, and they use same key index, try to use different key like 'item1' + index and 'item2' + index.
And, in your index.js file also have same problem.
Here is Demo forked from you: https://codesandbox.io/s/dark-darkness-xz0wf?file=/SubForm.js
As reactJs primary works on re rendering items every time you make a change the state of that component. It requires an unique key to track each and every item so that re-rendering can be optimized. Not all items needs to be re-render every time.
This warning essentially means that you have multiple items with same key names. In order to fix this you have to provide unique "key" attribute to each child component which is getting updated, components which can potentially trigger an re-render.
you don't need to use multiple times key={index} or key={"g"}
use once only.
Please try following
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Form, Input, Button, Space } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '#ant-design/icons';
const Demo = () => {
const onFinish = values => {
console.log('Received values of form:', values);
};
return (
<Form name="dynamic_form_nest_item" onFinish={onFinish} autoComplete="off">
<Form.List name="users">
{(fields, { add, remove }) => {
return (
<div>
{fields.map(field => (
<Space key={field.key} style={{ display: 'flex', marginBottom: 8 }} align="start">
<Form.Item
{...field}
name={[field.name, 'innerName']}
fieldKey={[field.fieldKey, 'innerName']}
>
<Input placeholder="passenger 1" />
</Form.Item>
<Form.Item
{...field}
name={[field.name, 'innerName2']}
fieldKey={[field.fieldKey, 'innerName2']}
>
<Input placeholder="passenger 2" />
</Form.Item>
<MinusCircleOutlined
onClick={() => {
remove(field.name);
}}
/>
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => {
add();
}}
block
>
<PlusOutlined /> Add field
</Button>
</Form.Item>
</div>
);
}}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, document.getElementById('container'));
I have a TextField that is disabled and I'm updating it with React Hooks useState by changing the value property of the TextField.
const [employee , setEmployee] = React.useState('')
<TextField
fullWidth
disabled
value={employee}
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<BackupIcon onClick={handleClick}/>
</InputAdornment>
),
}}
/>
It's only clickable on the Icon BackupIcon.
How can we make it clickable all over the TextField ?
Have you tried putting the onClick like this:
handleClick: function(e) {
this.setState({
textFieldValue: e.target.value
});
},
<TextField fullWidth
disabled
value={employee}
variant="outlined"
onChange={handleClick}
>
<BackupIcon />
<TextField />