Material UI textfield 'disable' prop won't update more than once - javascript

Trying to make a program where there are radio switches each equating to a different boolean value. Depending on the boolean value, it would either make the 'disable' prop on the textfield either true or false. My code allows for the button to be default selected as enabled editing and when I select disable it disables the textfield. However, if I click disable then try and click enable again it won't change the textfield from disable.
const [radioEdit, setRadioEdit] = React.useState(false);
<RadioGroup
value={radioEdit}
onChange={(e) => setRadioEdit(e.target.value)}
aria-labelledby="demo-radio-buttons-group-label"
name="radio-buttons-group"
row
>
<FormControlLabel
value={true}
control={<Radio />}
label="Disabled"
/>
<FormControlLabel
value={false}
control={<Radio />}
label="Enabled"
/>
<p>{String(radioEdit)}</p>
<TextField
id="outlined-basic"
variant="outlined"
size="small"
////////RIGHT HERE////////
value={data["companyname"]}
disabled={radioEdit}
/>
If the default state of radioEdit isn't 'false', it is automatically disabled (set to true or null) and won't let me update it multiple times.

The issue is with onChange you have defined with RadioGroup. Usually, we define
onChange={(e) => setRadioEdit(e.target.value)}
to set the value of text input onEventChangeHandler. But here it's for the Radio button. I was using typescript to find the answer for this and as soon as I copy-pasted your code it showed me the error at setRadioEdit(e.target.value) whereas the setRadioEdit should be boolean. The reason why TypeScript is super useful.
The answer is
onChange={() => setRadioEdit(!radioEdit)}
I'm toggling my state here onChange. So when we setRadioEdit as true, it's actually the radioEdit would be set as true. That's how the useState hook works. So by defining setRadioEdit(!radioEdit) we are toggling the state. If it's true it changes to false and vice versa.
Also you will have to close the </RadioGroup> component. Complete answer
const [radioEdit, setRadioEdit] = useState(false);
return (
<>
<RadioGroup
value={radioEdit}
onChange={(e) => setRadioEdit(!radioEdit)}
aria-labelledby="demo-radio-buttons-group-label"
name="radio-buttons-group"
row
>
<FormControlLabel value={true} control={<Radio />} label="Disabled" />
<FormControlLabel value={false} control={<Radio />} label="Enabled" />
</RadioGroup>
<p>{String(radioEdit)}</p>
<TextField
id="outlined-basic"
variant="outlined"
size="small"
disabled={radioEdit}
/>
</>
);

Related

TS RadioGroup returns type string on change handler rather than array of possible strings

I have an interface defined as so:
export interface StopData {
stopName: string,
stopType: 'stop' | 'waypoint'
}
I have a radio group for selecting the 'stopType', with two radio buttons.
One with the value 'stop' and one with 'waypoint'.
My onChange handler in the radio group looks like so: onChange={event => stopData['stopType'] = event.target.value}
However I've got a TS error:
Type 'string' is not assignable to type '"stop" | "waypoint"'
So even though I only have two radio buttons that match the strings my interface defines, the radiogroup still considers the type of event.target.value to be "any" rather than "'stop' | 'waypoint'"
How do I update stopData[stopType] without changing my interface to accept stopType: string. (Bonus if I can wrap them both updaters into the same change handler)
FYI: I'm using React, TS and MaterialUI for my tech stack.
The whole (relevant) code is as follows:
export default function CreateTripStop(stopData: StopData) {
return (
<Box className={classes.flexContainer}>
<TextField label='Stop Name' placeholder='Cupola Hut' name='stopName'
onChange={event => stopData['stopName'] = event.target.value} />
<RadioGroup row name='stopType' defaultValue="stop"
onChange={event => stopData['stopType'] = event.target.value}>
<FormControlLabel
value="stop"
label="Stop"
control={<Radio color="primary" />}
labelPlacement="start"
/>
<FormControlLabel
value="waypoint"
label="Waypoint"
control={<Radio color="primary" />}
labelPlacement="end"
/>
</RadioGroup>
</Box>
)
}
Just update like this:
onChange={event => stopData['stopType'] = event.target.value as StopData["stopType"]}

Material-UI Checkbox is not reset while using react-hook-form reset function

I'm using
"react-hook-form": "7.9.0",
And
"#material-ui/core": "4.11.4",.
I'm trying to reset some checkbox manually by clicking on regular button and using the reset (react-hook-form reset) method of react-hook-form.
For some reason I can see in react dev tools that the "checked" prop is changing to false but the SwitchBase (v icon) is still on.
You can see example: Over here
Thank you for your time.
The MUI <Checkbox /> component has a slighty different interface for setting a value. The RHF value has to be set with checked prop.
One important note: you have to set the value with checked={!!value} to avoid a warning about changing a component from uncontrolled to controlled in case that your defaultValue for someCheckbox is initially undefined.
export const Checkbox = ({
name,
label,
control,
labelPlacement,
disabled,
className
}) => {
return (
<FormControlLabel
label={label}
disabled={disabled}
labelPlacement={labelPlacement}
className={className}
control={
<Controller
name={name}
control={control}
rules={{ required: true }}
render={({ field: { value, ...field } }) => (
<MuiCheckbox
{...field}
checked={!!value}
/>
)}
/>
}
/>
);
};
The controller hook uses value (which is true/false) and not checked (which is what Checkbox of Material-UI expects) in order to set the check-status of the checkbox.
In order to fix your issue you can take the value from the field (inside the render property of the Controller) and set it to the checked property of the Checkbox:
control={
<Controller
name={name}
control={control}
rules={{ required: true }}
render={({ field }) => <MuiCheckbox {...field} checked={field['value'] ?? false} /> }
/>
}

React Material-UI get list of items checked

I have a React + Material-UI frontend. One section allows users to select items from a dropdown and I want the list of whatever the user selects. User can also click x and delete what they selected. How can I use state or some variable to get the final list of items?
CODE:
<Autocomplete
multiple
className={classes.inputField}
id="checkboxes-tags-demo"
options={userData}
disableCloseOnSelect
getOptionLabel={(option) => option.username}
renderOption={(option, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option.username}
</React.Fragment>
)}
style={{ width: 500 }}
renderInput={(params) => (
<TextField {...params} variant="outlined" label="Users" placeholder="Favorites" />
)}
/>
You can access the current selected values by adding an onChange to the Autocomplete component. I have used the Material UI example to show this in action (I am just logging the value to the console -- you will probably want to update a state item):
<Autocomplete
multiple
id="tags-standard"
options={top100Films}
onChange={(event, value) => {
// Printing the current value array
console.log(value);
}}
getOptionLabel={(option) => option.title}
defaultValue={[top100Films[13]]}
renderInput={(params) => (
<TextField
{...params}
variant="standard"
label="Multiple values"
placeholder="Favorites"
/>
)}
/>
The value is an array of objects:
There's a sandbox of this working here (again, it's using the standard Material UI example code). I have only updated the first Autocomplete component to print the value onChange.

Material-ui conditionally disabled radio

I'm building a React app with material-ui. I want to disable all my radio buttons in a RadioGroup when a certain event happens and re-enable them when the event goes away. (Say when I click a button, all radios are disabled, when I click the same button again, all radios are re-enabled.) I had the following conditional rendering snippet with a ternary operator which does the job but it looks really redundant. Is there a better way to do this? aka. Is there a way to make the prop (disable here) of a Material-ui component into a variable? Thanks!
const radioDisabled = false;
// Some mechanism here that could potentially
// change the value of radioDisabled
{ radioDisabled
?
<RadioGroup row
value={value}
onChange={(e)=>{setValue(e.target.value)}}
>
<FormControlLabel
value='1'
checked={value === '1'}
control={<Radio/>}
label='1'
/>
<FormControlLabel
value='2'
checked={value === '2'}
control={<Radio/>}
label='2'
/>
...
<FormControlLabel
value='n'
checked={value === 'n'}
control={<Radio/>}
label='n'
/>
</RadioGroup>
:
<RadioGroup row
value={value}
onChange={(e)=>{setValue(e.target.value)}}
>
<FormControlLabel
disabled // the only difference from above
value='1'
checked={value === '1'}
control={<Radio/>}
label='1'
/>
<FormControlLabel
disabled // the only difference from above
value='2'
checked={value === '2'}
control={<Radio/>}
label='2'
/>
...
<FormControlLabel
disabled // the only difference from above
value='n'
checked={value === 'n'}
control={<Radio/>}
label='n'
/>
</RadioGroup>
Here are my 2 options to get rid of the redundancy:
1st option is you can opt to remove the ternary conditional rendering and instead render the disabled prop based on a condition e.g. disabled={radioDisabled}
const [radioDisabled, setRadioDisabled] = React.useState(false);
<FormControlLabel
disabled={radioDisabled}
value="1"
checked={value === "1"}
control={<Radio />}
label="1"
/>
2nd option is you can iterate through the values/label of your radio input then assess if you need to disable or not, again, based on a condition
const [radioDisabled, setRadioDisabled] = React.useState(false);
const radioInputs = [
{
value: 1,
label: 1
},
{
value: 2,
label: 2
},
{
value: 3,
label: 3
}
];
{radioInputs.map((radioInput) => {
return (
<FormControlLabel
disabled={radioDisabled}
value={radioInput.value}
checked={value == radioInput.value}
control={<Radio />}
label={radioInput.label}
/>
);
})}
CodeSandBox: https://codesandbox.io/s/patient-worker-8mrq3?file=/src/App.js:1717-2041
import { useState } from 'react'
const [isRadioDisabled, setIsRadioDisabled] = useState(false)
<Button title='Disables RadioButtons'
onPress={() => setIsRadioDisabled(prevState => !prevState)} />
<RadioGroup row
value={value}
onChange={(e)=>{setValue(e.target.value)}}
>
<FormControlLabel
disabled={radioDisabled}
value='1'
checked={value === '1'}
control={<Radio/>}
label='1'
/>
<FormControlLabel
disabled={radioDisabled}
value='2'
checked={value === '2'}
control={<Radio/>}
label='2'
/>
<FormControlLabel
disabled={radioDisabled}
value='n'
checked={value === 'n'}
control={<Radio/>}
label='n'
/>
</RadioGroup>
Use React's useState hook to toggle between states to disable and enable FormControlLabels. Instead of using a variable or prop. Then, use a button to toggle between the true and false of the created state as shown above. There's no need to conditionally render them, since the disabled prop accepts a boolean to toggle from false to true.

MaterialUI Autocomplete - Avoid clearing input text filter when option is selected

I've imported the Autocomplete component from MaterialUI in my React project and using it as a multiple select with checkboxes: https://material-ui.com/components/autocomplete/#checkboxes
I noticed that when I type into the input to filter the list and then select a value, the filter inserted by the user resets. I want to avoid this and continue to multi-select with the filter instead of reinsert it every time. I didn't find any props in the component API to solve this.
Any suggestion?
That's my component code:
const VirtualAutocomplete = (props) => {
const classes = useStyles();
const textClasses = textStyles();
return (
<Autocomplete
id={props.id}
style={{ width: 'auto' }}
value={props.value}
limitTags={4}
noOptionsText="No records found."
classes={classes}
disableCloseOnSelect
ListboxComponent={ListboxComponent}
renderGroup={renderGroup}
onChange={props.onChange}
options={props.options}
filterOptions={startsWith}
multiple={props.multiple}
renderInput={(params) =>
<ThemeProvider theme={theme}>
<TextField {...params}
variant='outlined'
classes={{ root: textClasses.formControlRoot }}
InputLabelProps={{ classes: { root: textClasses.labelRoot } }}
label={props.label}
/>
</ThemeProvider>
}
renderOption={(option, { selected }) => (
<Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{option}
</Fragment>
)}
/>
);
}
Create a state that holds input value. Then on TextField onChange pass the function to change this state. Then on Autocomplete pass the props inputValue with that state content. You can also use disableCloseOnSelect props to Autocomplete so options box doesnt close on option selected.
Take a look at their docs about those props https://material-ui.com/pt/api/autocomplete/
Here is a example using their demo: https://codesandbox.io/s/material-demo-forked-pdh81?file=/demo.js:746-766

Categories

Resources