React js show/hide specific element using Material UI checkbox's state - reactjs

hello I'm trying to show and hide specific elements with checkbox material and what is happening now is when one checkbox is checked all the hidden divs are showing up.
You can see the problem here: https://stackblitz.com/edit/react-1ecdqb?file=demo.tsx
edit: I know that I need more variables in the state but I ask if there is a way to do it without state for each checkbox because there are gonna be 10 more checkboxes
const UninstallView = () => {
const [isChecked, setIsChecked] = useState(false);
const handleChange = event => {
if (event.target.checked) {
setIsChecked(true);
}
else {
setIsChecked(false);
}
}
return (
<div>
<FormGroup>
<FormControlLabel control={<Checkbox onChange={handleChange} />} label="simple isn't what I expected" />
{isChecked ? <TextField
id="filled-multiline-static"
label="What did you expect from simple?"
multiline
rows={4}
defaultValue=""
variant="filled"
/>
: '' }
</FormGroup>
<FormGroup>
<FormControlLabel control={<Checkbox onChange={handleChange} />} label="simple isn't working correctly" />
{isChecked ?
<div>
<h1>hello</h1>
</div>
: '' }
</FormGroup>
</div>
);

You are sharing 1 state across 2 checkboxes. You should have a separate state that holds the state for each checkbox.
This code may help:
const UninstallView = () => {
const [isFirstChecked, setIsFirstChecked] = useState(false);
const [isSecondChecked, setIsSecondChecked] = useState(false);
return (<div>
<FormGroup>
<FormControlLabel
control={<Checkbox onChange={() => setIsFirstChecked(!isFirstChecked)}/>}
label="simple isn't what I expected"/>
{isFirstChecked ? <TextField
id="filled-multiline-static"
label="What did you expect from simple?"
multiline
rows={4}
defaultValue=""
variant="filled"
/> : ''}
</FormGroup>
<FormGroup>
<FormControlLabel
control={<Checkbox onChange={() => setIsSecondChecked(!isSecondChecked)}/>}
label="simple isn't working correctly"/>
{isSecondChecked ? <div>
<h1>hello</h1>
</div> : ''}
</FormGroup>
</div>);
}
Sandbox example

Related

Validating radio button with React Hook Form

I have a custom radio button component in React looking like this:
export default function SingleSelectionButton(props) {
const {
values, name, required, errors, defaultData,
xs, md, register, infoBox, infoBoxContent, validateField } = props;
return (
<>
<Form.Group>
<Form.Label>
<FormattedMessage
id={name}
/>
</Form.Label>
{values.map((value) => (
<span key={value} className="SingleSelectionButton">
<input
{...register(name, { required: required}
id={name + value}
type="radio"
name={name}
value={value}
defaultChecked={defaultData.primary[name] === value}
/>
<label htmlFor={name + value}>
<FormattedMessage id={value} />
</label>
</span>
))}
</Form.Group>
</>
);
};
I call it like this, using an array for the different values:
<SingleSelectionButton
name="us_person"
md={6}
values={["yes", "no"]}
register={register}
required={true}
errors={errors}
validateField="no"
/>
The validation with required is working fine.
The problem is that I don't manage to validate a value in particular.
I have tried the following:
<input
{...register(name, { required: required, validate: value => value === validateField })}
id={name + value}
type="radio"
name={name}
value={value}
defaultChecked={defaultData.primary[name] === value}
/>
And also:
<input
{...register(name, { required: required, pattern: validateField })}
id={name + value}
type="radio"
name={name}
value={value}
defaultChecked={defaultData.primary[name] === value}
/>
So far no joy. What am I missing?

RHF not resetting values in an MUI checkbox group

I am trying to reset my form with the reset() method and, it is working partially. All of the form's fields get reset, except for the checkboxes group.
I have tried three different ways of implementing an MUI CheckboxesGroup but, I always get the same result.
You can see a working example in this sandbox
Checkbox one implementation
<FormControl error={error} required={required} component="fieldset">
<FormLabel component="legend">{label}</FormLabel>
<Controller
name={name}
render={({ field: { onBlur: rhfonBlur, onChange: rhfonChange } }) => (
<FormGroup onBlur={rhfonBlur}>
{Object.keys(options).map((key) => {
return (
<FormControlLabel
label={key}
key={key}
control={
<Checkbox onChange={(e) => rhfonChange(handleCheck(key))} />
}
/>
);
})}
</FormGroup>
)}
control={control}
/>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>
Checkbox two implementation
<FormControl error={error}>
<FormLabel component="legend">{label}</FormLabel>
<FormGroup>
{Object.keys(options).map((key) => {
return (
<FormControlLabel
key={key}
control={<Checkbox {...register(name)} />}
value={key}
label={key}
/>
);
})}
</FormGroup>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>
Checkbox three implementation
<FormControl required error={error} component="fieldset" variant="standard">
<FormLabel component="legend">{label}</FormLabel>
<Controller
control={control}
name={name}
render={({ field: { onChange: rfhonChange, value } }) => {
// console.log("value:", value);
return (
<FormGroup>
{Object.keys(state).map((key) => {
return (
<FormControlLabel
label={key}
key={key}
control={
<Checkbox
onChange={(e) => {
rfhonChange(handleChange(e));
}}
checked={state[key]}
name={key}
/>
}
/>
);
})}
</FormGroup>
);
}}
/>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>
Your third implementation was the correct way of doing it by using RHF's <Controller /> component. The reason why it wasn't working was because of the following:
you're not updating RHF's internal state for your field as you are only passing a function to the onChange handler. There is no need to use an additional useState here
your initial value is a boolean (false) but you are using an object inside the <CheckboxThree /> component
So basically you can simplify the component to the following (in you're example it isn't clear if the field value should be an object or an array containing the selected options - so the example i made is using an array):
const CheckboxThree = ({
control,
error,
helperText,
label,
name,
options
}) => {
return (
<FormControl required error={error} component="fieldset" variant="standard">
<FormLabel component="legend">{label}</FormLabel>
<Controller
control={control}
name={name}
render={({ field: { onChange, value, ref, ...field } }) => (
<FormGroup>
{Object.keys(options).map((key) => {
return (
<FormControlLabel
label={key}
key={key}
control={
<Checkbox
{...field}
name={key}
checked={value.some((option) => option === key)}
onChange={(event, checked) => {
if (checked) {
onChange([...value, event.target.name]);
} else {
onChange(
value.filter((value) => value !== event.target.name)
);
}
}}
inputRef={ref}
/>
}
/>
);
})}
</FormGroup>
)}
/>
<FormHelperText>{helperText}</FormHelperText>
</FormControl>
);
};

How to rerender fields selected by a dropdown?

im create a form using react-hook-from, and one of the fields that ive created is dynamic dropdown.
whenever im choosing one of the options im rendering some other text fields, depends on the the dropdown selection.
the problem is that when im submitting, im getting the fields of the other dropdown selection,
here is my code:
<FormGroup>
<Label>Automation Team</Label>
<Controller
name="team"
control={control}
as={Select}
options={ teams.map((team, index) => (
{ value: team.name, label: team.name}
)) }
defaultValue="Select component"
rules={{required: "Role is required" }}
isClearable
/>
</FormGroup>
<Row form>
{
getFields(teamComponent).map((field,index) => (
<Col key={index} md={4}>
<Label for={field}>{field}</Label>
<Input name={field} placeholder="with a placeholder" innerRef={register({required: true})}/>
{errors?.field?.types?.required &&
<small className="text-danger">Database name required</small>}
</Col>
) )
}
</Row>
so the problem was that I didnt used useEffect and didnt unregister from
last fields that was rendered.
Not the cleanest code bit it works for me :) .
so I refactored my code to look like this:
function TicketForm () {
const { register, unregister, handleSubmit, reset, formState: {isSubmitSuccessful, errors}, control, watch } = useForm({
mode: "onBlur",
criteriaMode: "all"
});
const teamComponent = watch("team")
const {isAuthenticated} = useAuthentication();
const [teams, setTeams] = useState([])
const [fields, setFields] = useState(0);
useEffect( () => {
if (isAuthenticated === true) {
getConfigurations(async response => {
setTeams(response.data.teams.Teams)
})
}
if (teamComponent )
onTeamChange(teamComponent)
}, [teamComponent])
const handleRegistration = async data => {
console.log(data)
}
const onTeamChange = c => {
if (!c) {
setFields([])
return
}
if (fields) {
fields.map((field,index) => {
unregister(field)
})
}
const result = teams.filter((team, index) => {
if(team.name === c.value) {
return team
}
})
if (result) {
setFields(result[0].Fields)
console.log(fields)
}
}
return (
<Form onSubmit={handleSubmit(handleRegistration)}>
<FormGroup>
<Label>Summary</Label>
<Input name="summary" innerRef={register({required: true})} />
{errors?.summary?.types?.required && <small className="text-danger">summary required</small>}
</FormGroup>
<FormGroup>
<Label>Description</Label>
<Input
type="textarea"
name="description"
innerRef={register({required: true})}
/>
{errors?.description?.types?.required && <small className="text-danger">description required</small>}
</FormGroup>
<FormGroup>
<Label for='team'>Automation Team</Label>
<Controller
control={control}
name="team"
innerRef={register({required: true})}
as={Select}
options={ teams.map((team, index) => (
{ value: team.name, label: team.name}
)) }
defaultValue=""
isClearable
/>
</FormGroup>
<Row form>
{
fields && fields.map((field,index) => {
//const fieldName = `field[${index}]`;
return (
<fieldset name={field} key={index}>
<Label for={field}>{field}</Label>
<Input name={field} placeholder="" innerRef={register({required: true})}/>
</fieldset>
)
}/*(
<Col key={index} md={4}>
<Label for={field}>{field}</Label>
<Input name={field} placeholder="with a placeholder" innerRef={register({required: true})}/>
{errors?.field?.types?.required &&
<small className="text-danger">Database name required</small>}
</Col>
)*/ )
}
</Row>
<Button type='submit' color="primary">Submit</Button>
</Form>
);}export default TicketForm;

How to use InputProps of TextField in <input /> element

Is there any way to use the InputProps in <input /> element instead of <TextField /> of Material UI?
My Input.js component where I have used InputProps:
const Input = ({name, handleChange, type, handleShowPassword}) => {
return(
<input
name={name}
onChange={handleChange}
require
type={type}
InputProps={name === 'password' && {
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={handleShowPassword}>
{type === 'password' ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
),
}}
/>
)
}
export default Input
SignUp.js:
const SignUp = () => {
return (
<div>
<Input name="firstName" handleChange={handleChange} />
<Input name="password" type={showPassword ? 'text' : 'password'} handleShowPassword={handleShowPassword} />
</div>
)
}
export default SignUp
Please any help would be appreciated or feel free to share if there are any other ways to achieve the result.
My suggestion is you need to use Tailwindcss, and install tailwindcss/forms package.
I just found one tailwind component that are using
<input/>
for that functionality.
Here's the link for that : https://www.hyperui.dev/components/marketing/forms

Passing Formik input values in nested forms using Material UI

I'm currently having problems when using Formik with MaterialUI forms. Specifically,
I am having trouble passing Formik input values in nested forms using Material UI and having a small issue where Formik.handleChange is changing the value from number to string.
I have multiple forms that are split with Stepper component in Material UI. Since I am still learning, I tried to wrap Formik on one of the steps (later on I am going to wrap the whole stepper). Here's how it looks:
{activeStep === 0 && (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 400);
}}
>
{formik => (
<form onSubmit={formik.handleSubmit}>
<div className="col-xl-6">
<Portlet fluidHeight>
<PortletHeader title="Incident Type" />
<PortletBody>
<IncidentSelect formik={formik} />
</PortletBody>
</Portlet>
</div>
</form>
)}
</Formik>
)}
The problem is inside the IncidentSelect form, Formik handleChange does not seem to change the selected radioButton. When I inspected with React Developer Tools in Chrome it seems that Formik.handleChange is changing the value from 0 to "0". How do I fix this?
Also, following the tutorial, I'm unsure how I can abstract my components? Note that DateTimePicker is using material-ui/pickers. I'm not sure how I am going to pass the value to Formik.
Any help is appreciated.
Thanks
function IncidentSelect(props) {
const [value, setValue] = React.useState("female");
const handleRadioChange = e => {
console.log(props.formik.getFieldProps("incidentType"));
setValue(e.target.value);
};
return (
<>
<FormControl component="fieldset">
<FormLabel component="legend" required>
Incident Type:
</FormLabel>
<RadioGroup
aria-label="Incident Type"
name="incidentType"
value={value}
onChange={handleRadioChange}
{...props.formik.getFieldProps("incidentType")}
>
<FormControlLabel
value={0}
control={<Radio />}
label="Injury To Guest"
/>
<FormControlLabel
value={1}
control={<Radio />}
label="Injury To Worker"
/>
<FormControlLabel
value={2}
control={<Radio />}
label="Incident / Accident"
/>
<FormControlLabel
value={3}
disabled
control={<Radio />}
label="OSH / Kids Camp"
/>
</RadioGroup>
</FormControl>
<DateTimePicker
label="Signed Date"
variant="outlined"
className={classes.margin}
value={selectedDate}
onChange={handleDateChange}
/>
</>
);
}
As stated in the tutorial, it is easier to abstract the component that you want to use. Giving you chances to reuse them later in your application and more readable code.
Formik provides you with useFields API to get the props of the field via hooks. useFields is looking for the name props of your component to find the corresponding field.
Thus RadioGroup from MaterialUI can be extracted as follows:
export const IncidentRadioButton = ({ options, ...props }) => {
const [field, meta] = useField(props);
return (
<>
<RadioGroup {...field} {...props}>
{options.map((option, index) => (
<FormControlLabel
control={<Radio />}
label={option.name}
value={option.value}
key={index}
/>
))}
</RadioGroup>
{meta.touched && meta.error ? (
<div className="error">{meta.error}</div>
) : null}
</>
);
};
Then you can use the options prop to put your data accordingly

Resources