Below given code is using material-ui with reactjs. I am filtering the information of products through this sidebar function. In which there are 2 filers size and color. When we click on Expansion Panel, options will displayed and than we click on desired option (which is not returning the selected color). On the other hand same Expansion Panel is returning the correct selected "size". What i can do so that onClick={colorReducer} function return correct color. Any help will be appreciated
const Sidebar = () => {
const [size, setSize] = React.useState(0);
const [color, setColor] = React.useState("");
const sizeReducer = (e) => {
setSize(e.target.value); //This is working fine
};
const colorReducer = (e) => {
console.log(color); //It gives 0 as output, instead of selected color (type string)
setColor(e.target.value);
};
return (
<div className={classes.div}>
<ExpansionPanel>
<ExpansionPanelSummary>
<InputLabel id="label">Size</InputLabel>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<ListItem>
<MenuItem value="6" onClick={sizeReducer}>
6
</MenuItem>
<MenuItem value="7" onClick={sizeReducer}>
7
</MenuItem>
<MenuItem value="8" onClick={sizeReducer}>
8
</MenuItem>
<MenuItem value="9" onClick={sizeReducer}>
9
</MenuItem>
</ListItem>
</ExpansionPanelDetails>
</ExpansionPanel>
<ExpansionPanel>
<ExpansionPanelSummary>
<InputLabel id="label">Color</InputLabel>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<ListItem>
{setColors.map((val) => (
<MenuItem value={val} onClick={colorReducer}> //This is not returning "0", instead of color
{val}
</MenuItem>
))}
</ListItem>
</ExpansionPanelDetails>
</ExpansionPanel>
</div>
);
};
export default Sidebar;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<MenuItem> renders to <li> element. Interestingly, value is an allowed attribute for li but it can only take numbers. That's why it works for setSize but doesn't for setColor.
A proper way to do what you want is not to relegate to DOM but stay in React-land. So, instead of reading from event.target, you should provide value in a handler:
<MenuItem onClick={() => setColor(val)}>
{val}
</MenuItem>
{setColors.map((val) => (
<MenuItem value={val.toString()} onClick={colorReducer}>
{val}
</MenuItem>
))}
Update this part, you've passed int value in above mentioned part of code.
Related
I'm using React with Chakra UI for this.
I'm having an issue with my radio buttons. I have two groups of radio buttons, second group is showed dynamically based on the first group selection. However, first group works fine, but when selecting an option from the second group it doesn't get marked, but looking at the state of the component it does pass the value.
Below is the code of my component.
const SuperAttack = ({ handleChange }: Props) => {
const [selectedMutiplier, setSelectedMultiplier] = useState<SAMultiplier>();
const handleMultiplierSelect = (name: string) => {
const selected = SAMultipliers.filter((multiplier) => {
return multiplier.name === name;
});
setSelectedMultiplier(selected[0]);
};
return (
<Box
border="1px"
borderColor="gray.200"
borderRadius={10}
p="20px"
mb="10px"
>
<FormControl>
<FormLabel>Super attack multiplier</FormLabel>
<RadioGroup name="attack" onChange={handleMultiplierSelect}>
<Stack direction="row">
{SAMultipliers.map((multiplier) => {
return <Radio value={multiplier.name}>{multiplier.name}</Radio>;
})}
</Stack>
</RadioGroup>
</FormControl>
{selectedMutiplier ? (
<FormControl>
<FormLabel>Select SA level</FormLabel>
<RadioGroup name="multiplier" onChange={handleChange}>
<Stack direction="row">
{selectedMutiplier.SA.map((multiplier) => {
return (
<Radio value={multiplier.value}>{multiplier.name}</Radio>
);
})}
</Stack>
</RadioGroup>
</FormControl>
) : null}
</Box>
);
};
export default SuperAttack;
Thanks in advance
I have a case where the I have 3 items, and at in the case where is the first item, it should be displaying only the first item, and not allow the user to select 2nd and 3rd, but in case wher isItFirt = false then the user should be able to choose from the list. I wrote the minimal reproducible example as shown below:
import * as React from "react";
import {
Typography,
Button,
Dialog,
Box,
Select,
InputLabel,
FormControl,
MenuItem,
SelectChangeEvent
} from "#mui/material";
enum MyOptions {
FIRST = 1,
SECOND = 2,
THIRD = 3
}
export default function App() {
const [open, setOpen] = React.useState(true);
const [myOptions, setMyOptions] = React.useState(MyOptions.SECOND as number);
const handleChange = (event: SelectChangeEvent) => {
let nr = parseInt(event.target.value, 10);
setMyOptions(nr);
};
const isItFirst: boolean = false;
const handleClose = () => {
setOpen(false);
};
const somethingHappens = () => {
console.log("clicked: ", myOptions);
setOpen(false);
};
React.useEffect(() => {
if (isItFirst) {
setMyOptions(MyOptions.FIRST as number);
}
}, [isItFirst]);
return (
<div>
<Button
variant="contained"
size="small"
onClick={() => {
setOpen(true);
}}
>
Display dialog
</Button>
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box>
<Typography id="modal-modal-title" variant="h6" component="h4">
Select one of the options
</Typography>
<FormControl>
<InputLabel id="1">Options</InputLabel>
<Select
labelId=""
id=""
value={myOptions}
label="Options"
onChange={(e: any) => handleChange(e)}
>
{isItFirst ? (
<MenuItem value={MyOptions.FIRST}>This is first</MenuItem>
) : (
<div>
<MenuItem value={MyOptions.SECOND} key={MyOptions.SECOND}>
This is second
</MenuItem>
<MenuItem value={MyOptions.THIRD} key={MyOptions.THIRD}>
This is third
</MenuItem>
</div>
)}
</Select>
</FormControl>
</Box>
<Button
variant="contained"
size="small"
onClick={() => {
somethingHappens();
}}
>
Select
</Button>
</Dialog>
</div>
);
}
This is the error output:
MUI: You have provided an out-of-range value `1` for the select component.
Consider providing a value that matches one of the available options or ''.
The available values are "".
And this is the dialog box that is shown in the case when isItFirst === false, I do not understand why it is shown as blank when I set the state of myOptions with the help of useEffect.
According to this document for children prop of Select
The option elements to populate the select with. Can be some MenuItem when native is false and option when native is true.
⚠️The MenuItem elements must be direct descendants when native is false.
So technically, we cannot pass div or any other elements to wrap MenuItem.
For the fix, you can consider to use filter and map with a pre-defined option like below
const options: {value: MyOptions, label: string}[] = [
{value: MyOptions.FIRST, label: "This is first"},
{value: MyOptions.SECOND, label: "This is second"},
{value: MyOptions.THIRD, label: "This is thrid"}
]
Here is how we apply options to Select
<Select
labelId=""
id=""
value={myOptions}
label="Options"
onChange={handleChange}
key="first-select"
>
{options
.filter((option) =>
isItFirst
? option.value === MyOptions.FIRST
: option.value !== MyOptions.FIRST
)
.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</Select>
I'm creating a material-UI dialogue form that posts data to my API. One of the fields in my backend database is binary and takes in only two possible options. How can I reflect that in my dialogue code below?
Here is my Fulltrace Back error:
Material-UI: You have provided an out-of-range value undefined for
the select (name="category") component. Consider providing a value
that matches one of the available options or ''. The available values
are personal, social.
The possible options for this specific field are either personal or social.
I tried doing this, letting my dialogue push the correct responses:
<MenuItem value={'personal'}> personal </MenuItem>
<MenuItem value={'social'}> social </MenuItem>
But that does not work and I understand why. Just not sure how to solve the error now as I'm not too savvy with React/JS in general.
export default function CreateBucket() {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const history = useHistory();
const initialFormData = Object.freeze({
name: '',
category: '',
about: '',
});
const [formData, updateFormData] = useState(initialFormData);
const handleChange = (e) => {
updateFormData({
...formData,
// Trimming any whitespace
[e.target.name]: e.target.value.trim(),
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(formData);
axiosInstance
.post(`create/bucket`, {
name: formData.name,
category: formData.category,
about: formData.about,
})
.then((res) => {
history.push('/');
console.log(res);
console.log(res.data);
});
};
const classes = useStyles();
return(
<Fragment>
<Fab color="primary" aria-label="add" onClick={handleClickOpen} variant="extended">
<AddIcon className={classes.extendedIcon}/>
Create Bucket
</Fab>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Create your Bucket</DialogTitle>
<DialogContent>
<DialogContentText>
Get started! Make your bucket by telling us a little bit more.
</DialogContentText>
<form>
<FormControl>
<InputLabel> What type of Bucket </InputLabel>
<Select
id="category"
label="Bucket Type"
name="category"
fullWidth
required
variant="filled"
onChange={handleChange}
margin="normal"
className={classes.formControl}
>
<MenuItem value={'personal'}> personal </MenuItem>
<MenuItem value={'social'}> social </MenuItem>
</Select>
</FormControl>
</form>
</DialogContent>
<DialogActions>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={handleSubmit}
>
Create Bucket
</Button>
</DialogActions>
</Dialog>
</Fragment>
);
}
How can I implement changes to solve my traceback error?
Make sure you provide value prop to <Select> component, put value="" in case you don't want any of the options to be selected by default or value="personal" in case you want personal to be selected by default.
Her is mine with value={formData.category} it takes the value selected from state.
export default function CreateBucket() {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const history = useHistory();
const initialFormData = Object.freeze({
name: '',
category: '',
about: '',
});
const [formData, updateFormData] = useState(initialFormData);
const handleChange = (e) => {
updateFormData({
...formData,
// Trimming any whitespace
[e.target.name]: e.target.value.trim(),
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(formData);
axiosInstance
.post(`create/bucket`, {
name: formData.name,
category: formData.category,
about: formData.about,
})
.then((res) => {
history.push('/');
console.log(res);
console.log(res.data);
});
};
const classes = useStyles();
return(
<Fragment>
<Fab color="primary" aria-label="add" onClick={handleClickOpen} variant="extended">
<AddIcon className={classes.extendedIcon}/>
Create Bucket
</Fab>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Create your Bucket</DialogTitle>
<DialogContent>
<DialogContentText>
Get started! Make your bucket by telling us a little bit more.
</DialogContentText>
<form>
<FormControl>
<InputLabel> What type of Bucket </InputLabel>
<Select
id="category"
label="Bucket Type"
name="category"
fullWidth
required
variant="filled"
onChange={handleChange}
margin="normal"
value={formData.category}
className={classes.formControl}
>
<MenuItem value={'personal'}> personal </MenuItem>
<MenuItem value={'social'}> social </MenuItem>
</Select>
</FormControl>
</form>
</DialogContent>
<DialogActions>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={handleSubmit}
>
Create Bucket
</Button>
</DialogActions>
</Dialog>
</Fragment>
);
}
It is exactly what it says on the tin:
Material-UI: You have provided an out-of-range value undefined for the select (name="category") component. Consider providing a value that matches one of the available options or ''. The available values are personal, social.
When you create a Select it wants you to provide initial value, which can be personal, social or an empty string. You don't provide any value, so it get confused
<Select
id="category"
value={formData.category}
label="Bucket Type"
will make it stop complaining.
None of these solved my issue where I wanted to have a placeholder but I have a state value and an onChange event inside my textfield. Once you set the initial state you cannot have a placeholder.
My fix below is to have the value of 'place holder text' in initial state and inside the list of options. I disabled and grayed out the value once initially loaded so it looks like a placeholder.
<TextField
margin="dense"
select
fullWidth
placeholder='Fuel Type'
value={fuelType}
onChange={handleFuelChange}
// defaultValue="Fuel Type"
type="text"
name="fuelType"
>
{options3.map((option) => {
return (option.label === "Fuel Type" ? <MenuItem key={option.label} disabled value={option.label + "," + option.carbonFactor}>
<span style={{ color: "rgb(156, 156, 156)" }}>{option.label}</span>
</MenuItem> : <MenuItem key={option.label} value={option.label + "," + option.carbonFactor}>
{option.label}
</MenuItem>)
})}
</TextField>
When I click item one button in first tab it should disable next two tabs.
When I click it back it should enable the other two tabs.
Same functionality should happen for other tabs.
Right now I disabled the second tab by using disabled property.
Can you guys tell me how to fix it.
Providing my code snippet and sandbox below.
https://codesandbox.io/s/material-demo-ulrv5
export default function SimpleTabs() {
const classes = useStyles();
const [value, setValue] = React.useState(0);
function handleChange(event, newValue) {
setValue(newValue);
}
function selectButton(e) {
//const selectedButton = e.currentTarget;
const selectedButton = e.target;
console.log("selectedButton--->", selectedButton);
this.setState({ selectedButton: false });
}
return (
<div className={classes.root}>
<AppBar position="static">
<Tabs value={value} onChange={handleChange}>
<Tab label="Item One" />
<Tab label="Item Two" disabled />
<Tab label="Item Three" />
</Tabs>
</AppBar>
{value === 0 && (
<TabContainer>
Item One
<Button
variant="contained"
color="primary"
className={classes.button}
onClick={selectButton}
>
item one
</Button>
</TabContainer>
)}
{value === 1 && (
<TabContainer>
<Button
variant="contained"
color="primary"
className={classes.button}
onClick={selectButton}
>
item one
</Button>
</TabContainer>
)}
{value === 2 && (
<TabContainer>
<Button
variant="contained"
color="primary"
className={classes.button}
onClick={selectButton}
>
item one
</Button>
</TabContainer>
)}
</div>
);
}
If I understand you correctly, you basically need a boolean variable so click on the inner buttons will toggle it.
If so, like #charlietfl mention, you can't use this.setState in a function component but with useState you can set the state even though with the method (the 2nd argument) you get from useState.
So the solution will be to initialize the variable:
const [isDisabled, disableButtons] = React.useState(true);
Set conditional disabled attribute to the 2nd and 3rd tabs
<Tab label="Item Two" disabled={isDisabled} />
<Tab label="Item Three" disabled={isDisabled} />
And in the selectButton() function, toggle it
function selectButton() {
//const selectedButton = e.currentTarget;
disableButtons(!isDisabled);
// this.setState({ selectedButton: false });
}
Working example
Write a function which would return a disabled text or empty string accordingly, and write {this.getClass()} instead of disabled according to condition
Hi im new in react and i have been using react-datepicker but i got this problem, i made a custom button that when is clicked it should display the datepicker and well it does it, the problem is that it is trapped inside the menu of the button, and what i need is it to popout outside the menu (actually i need to close the menu when selected and just see the datepicker)
any kind of help would be apreciated
as you can see my datepicker is inside a menu
this is my menu and datepicker code
<Menu
className="main-toolbar__menu"
anchorEl={this.state.anchors.options_settings}
open={Boolean(this.state.anchors.options_settings)}
onClose={() => this.handleDropdownClose('options_settings')}
>
<MenuItem
onClick={() => {
this.toggleCalendar()
}}
>
<DatePicker
customInput={<ExampleCustomInput />}
selected={this.state.startDate}
onChange={this.handleChange}
/>
</MenuItem>
<MenuItem
onClick={() => {
this.handleModal('addNotificationModal', true)
this.handleDropdownClose('options_settings')
}}
>
<span>Notification Preferences</span>
</MenuItem>
</Menu>
You can Move your datepicker outside the Menu component and toggle it based on the toggle logic and a ternary expression.
state = {
showCalendar:false
}
toggleCalendar = () => {
this.setState({showCalendar:!this.state.showCalendar})
//or whatever logic you have for toggleCalendar already
}
{this.state.showCalendar ? <DatePicker
customInput={<ExampleCustomInput />}
selected={this.state.startDate}
onChange={this.handleChange}
/> : null }
<Menu
className="main-toolbar__menu"
anchorEl={this.state.anchors.options_settings}
open={Boolean(this.state.anchors.options_settings)}
onClose={() => this.handleDropdownClose('options_settings')}
>
<MenuItem
onClick={() => {
this.toggleCalendar()
}}
>
</MenuItem>
<MenuItem
onClick={() => {
this.handleModal('addNotificationModal', true)
this.handleDropdownClose('options_settings')
}}
>
<span>Notification Preferences</span>
</MenuItem>
</Menu>