I'm using MaterialUI components and i map through an array of object to generate some checkboxes as shown below.
const wrapper = () => {
...
return(
<FormGroup>
{Object.keys(products).map((key) => {
return <FormControlLabel label={products[key].name} key={key} control={
<CheckBox value={products[key].name} />
}
})
);
}
So given the code above. Let's say the products array has 3 object. Whenever i check a checkbox, i want all others to get a checked false and the one i checked a check true.
I'm using the state Hook so the code above is a functional component.
Material-UI Checkbox Component takes a prop checked of boolean type.
What you can do is maintain the flag for each checkbox and whenever any checkbox if pressed, just make its value in state true, and false the other and pass that state checked prop of each checkbox.
BTW, the behavior you want to achieve here is not done by checkboxes, it is the behavior of Radio Button, where the user can select only one option at a time while checkboxes are used for multiple selections.
(I believe material-ui checkbox documentation has a very clear explanation on this, Let me know if you can keep up by yourself or you want sample code too.)
I would suggest using Radio buttons instead of checkBoxes in this case.
https://material-ui.com/components/radio-buttons/
No more than one radio button can be selected at a time which is what you are describing.
Related
I'm creating a React form with Material UI. My goal is to force the user to answer all the questions before they can click the download button. In other words I'm trying to leave the download button in the disabled state until I can determine that values are set on each field. I've tried to get this working with and without react-hook-form.
What I've tried
Without react-hook-form...
I have my example in coding sandbox here:
https://codesandbox.io/s/enable-download-button-xwois4?file=/src/App.js
In this attempt I abandoned react-hook-form and added some logic that executes onChange. It looks through each of the formValues and ensures none of them are empty or set to "no answer". Like this:
const handleInputChange = (e) => {
// do setFormValues stuff first
// check that every question has been answered and enable / disable the download button
let disableDownload = false;
Object.values(formValues).forEach((val) => {
if (
typeof val === "undefined" ||
val === null ||
val === "no answer" ||
val === ""
) {
disableDownload = true;
}
});
setBtnDisabled(disableDownload);
The problem with this approach, as you'll see from playing with the UI in codesandbox, is that it requires an extra toggle of the form field value in order to detect that every field has been set. After the extra "toggle" the button does indeed re-enable. Maybe I could change this to onBlur but overall I feel like this approach isn't the right way to do it.
Using react-hook-form
With this approach...the approach I prefer to get working but really struggled with, I ran into several problems.
First the setup:
I removed the logic for setBtnDisabled() in the handleInputChange function.
I tried following the example on the react-hook-form website for material ui but in that example they're explicitly defining the defaultValues in useForm where-as mine come from useEffect. I want my initial values to come from my questionsObject so I don't think I want to get rid of useEffect.
I couldn't figure out what to do with {...field} as in the linked material ui example. I tried dropping it on RadioGroup
<Controller
name="RadioGroup"
control={control}
rules={{ required: true }}
render={({ field }) => (
<RadioGroup
questiontype={question.type}
name={questionId}
value={formValues[questionId]}
onChange={handleInputChange}
row
{...field}
>
but I get an MUI error of : MUI: A component is changing the uncontrolled value state of RadioGroup to be controlled.
Also, I don't see that useForm's state is changing at all. For example, I was hoping the list of touchedfields would increase as I clicked radio buttons but it isn't. I read that I should pass formState into useEffect() like this:
useEffect(() => {
const outputObj = {};
Object.keys(questionsObject).map(
(question) => (outputObj[question] = questionsObject[question].value)
);
setFormValues(outputObj);
}, [formState]);
but that makes me question what I need to do with formValues. Wondering if formState is supposed to replace useState for this.
I'm building a form with React-Hook-Form and Material-UI. Each Material-UI form component is wrapped into a react-hook-form Controller component.
The user has the option to populate the form automatically with different sets of pre-defined values when clicks on a button. I'm trying to use setValue() to achieve that and it seems to work fine for text input and select. However, for autocomplete the new value is not rendered although when submitting the form it is correctly sent. Also, when a text area is rendered the content seems to be mixed with the label.
Here is a complete example:
CodeSandbox Link
You need to cotroll the value of AutoComplete by passing value attribute to AutoComplete component, But since you are using string value you couldn't pass value to AutoComplete directly and you should to find the target option like this:
<Autocomplete
...
value={typeof value === 'string' ? options.find(o => o.name === value) : (value || null)}
/>
Your textarea problem occurs because your value by default is undefined and material considers the textare as uncotrolled input, to solve it you can pass empty string when value is undefined, like this:
<TextField
...
value={value || ''}
...
/>
I have this code:
<Dropdown
selectedKey={someKeyInState}
onChange={(e,option) => {
// check if the dropdown should be updated
if(someCondition){
// update selected key
}
else {
// don't update selected key
}
}}
options={someOptions}
/>
I want to block updating the selected key if a certain condition is met.
But, Dropdown visually shows the option that I clicked on as selected.
How do I prevent this behavior?
If you are using react, you should not do in this way. Use a global state optionState and try to use conditional checks which state should be shown to use onChange method is triggered. In order to do this. First extract your conditional check to separate function.
myFunction = () => {
// do conditional check and setState for optionState
}
--------------------------
onChange={myFunction}
Then when you select pop the key and value from the option state and update to the selectedState, now react automatically update the state and UI
The problem was my state for selectedKey was undefined it nothing is selected. When you pass in undefined you're basically saying to fabric to control the selectedKey state on it's own. I fixed by passing null instead of undefined. That made the Dropdown fully controlled.
I'm using React Widgets for the Dropdown List, I want to customize the onChange to get my state as the selected value:
const { selected } = this.state;
return <DropdownList {...rest}
data={fitData} onSearch={this.search}
onChange={this.change}
/>
The onChange simply like this:
change(selected) {
this.setState({selected});
}
It works, but when I close the dropdown list without selecting any value, the selected still chosen. What could I do to remove the selected if I don't choose any value?
The onChange event will get called only on a value change.
if you want to listen to the 'closed without selecting' You should listen to the onToggle event.
You can call the same method as you did on the onChange and check the selected value is null/undefined.
onToggle={this.change}
How can we display the selected value just below the input box.
Use Case:
We are using multiple select of react-select , when we select the value from the select box , it comes inside the input box as selected. Can we have a method or something to get the selected values outside the input box (just below it)
Thanks in Advance!
I had a similar problem and I solved it creating a wrapper of react-select component and adding a state to my custom component. When the react-select changes I added the selected items to my component state and I show the in a custom div below, there you can add the styles that you want. Here an example of my approach: https://codesandbox.io/s/2kyy4998y
Hope this helps you.
Regards
I recently had to do this for a project I'm working on and wrote up how I did it here. The gist is that you need a wrapper component
// SelectWrapper.js
import ReactSelect from 'react-select'
const SelectWrapper = (props) => {
const { isMulti, value } = props;
return (
<div>
{isMulti ? value.map((val) => <span>{val.label}</span>) : null}
<Select {...props} controlShouldRenderValue={!isMulti} />
</div>
)
}
The very important part here is the controlShouldRenderValue prop which we disable when isMulti is true so the select dosn't show any selected values instead letting us take care of that