Why my 'Select' components in react are synchronized together? - reactjs

I am currently using materialui to create a submission page. I have created two 'Select' components for the page, and there is a problem that if I choose an option from the second 'Select' component, the option from the first 'Select' component will be refreshed. I was thinking there is something wrong with the 'onChange', yet I have no idea about the cause for this problem. I want to select all the options from the 'Select' components before submitting the data at once.
Also, is there a way to disable the second 'Select' components before entering an option other than 'None' in the first 'Select' components, and enable it after a valid option has been entered?
Thank you very much for your time and help!
import React, { Component } from 'react';
import { Form, Field } from 'react-final-form';
import { OnChange } from 'react-final-form-listeners';
import { makeStyles, InputLabel, FormControl, Typography, Paper, Link, Grid, Button, CssBaseline, MenuItem, TextField, Select, Box, FormHelperText} from '#material-ui/core';
const useStyples = makeStyles((theme) => ({
formControl: {
margin: theme.spacing(2),
minWidth: 400
},
typo1: {
padding: 30,
margin: 'auto',
maxWidth: 600
},
typo2: {
padding: 0,
margin: 'auto',
maxWidth: 600
},
}))
export default function CreatePage() {
const classes = useStyples()
const [value, setValue] = React.useState('')
const handleChange = (event) => {
setValue(event.target.value)
}
return (
<div>
<Typography variant='h4' align='center' component='h1' gutterBottom className={classes.typo1}>
Data Submission Form
</Typography>
<Typography variant='h5' align='center' component='h2' gutterBottom className={classes.typo2}>
Material-UI
</Typography>
<Box display='flex' flexDirection='row'>
<Box>
<FormControl required variant='filled' className={classes.formControl}>
<InputLabel shrink>Defect</InputLabel>
<Select
labelId='defect-select1'
id='defect-select1'
value={value}
displayEmpty
onChange={handleChange}>
<MenuItem value=''>None</MenuItem>
<MenuItem value={'Crack'}>Crack</MenuItem>
<MenuItem value={'Scratch'}>Scratch</MenuItem>
<MenuItem value={'Rough Surface'}>Rough Surface</MenuItem>
<MenuItem value={'Spalling'}>Spalling</MenuItem>
</Select>
<FormHelperText>Select Defect for Position 1 - Required</FormHelperText>
</FormControl>
</Box>
<Box>
<FormControl required variant='filled' className={classes.formControl}>
<InputLabel shrink>Tool</InputLabel>
<Select
labelId='tool1'
id='tool1'
displayEmpty
value={value}
onChange={handleChange}>
<MenuItem value=''>None</MenuItem>
<MenuItem value={'Tool 1'}>Tool 1</MenuItem>
<MenuItem value={'Tool 2'}>Tool 2</MenuItem>
<MenuItem value={'Tool 3'}>Tool 3</MenuItem>
<MenuItem value={'Tool 4'}>Tool 4</MenuItem>
</Select>
<FormHelperText>Select Tool Decision for Position 1 - Required</FormHelperText>
</FormControl>
</Box>
</Box>
</div>
);
}
I would be glad if someone can tell me the root cause for the problem. An additon of a solution for my question would be better!

Well, it seems that you only have 1 value :
const [value, setValue] = React.useState('')
And although you have duplicated the <Select /> component you didn't duplicate the value...
You'll need a second "value" and a second handleChange which will update that second value
const [value2, setValue2] = React.useState('');
const handleChange2 = (event) => {
setValue2(event.target.value)
}

Related

I'm having problems putting all my items in a row with flex in REACT using MUI

I'm currently trying to do the View of the frontend of my React application.
I want to put a filter panel and I want to put my select menus in a row starting by the beginnig kind of like this:
enter image description here
And this is how actually is (name, price range and rate are the target boxes):
The last one get squashed.
Here it how it looks in the Chrome inspector:
enter image description here
enter image description here
And this is the code of the component:
`
import * as React from 'react';
import Box from '#mui/material/Box';
import TextField from '#mui/material/TextField';
import MenuItem from '#mui/material/MenuItem';
import Select from '#mui/material/Select';
import InputLabel from '#mui/material/InputLabel';
import { FormControl } from '#mui/material';
export default function Filters(){
//This is just logic for the boxes
const [stars, setStars] = React.useState('');
const priceRanger = [
{
value: '0-15',
label: '0-15',
},
{
value: '16-30',
label: '16-30',
},
{
value: '31-60',
label: '31-60',
},
{
value: '61-100',
label: '61-100',
},
{
value: '101',
label: '101-',
},
];
const [rate, setRate] = React.useState('0-15');
return (
<Box
component="form"
sx={{
'& > :not(style)': { m: 3,alignItems: 'flex-start', width: '25ch' },
}}
noValidate
autoComplete="off"
>
{/* First item*/}
<TextField id="filled-basic" label="Name" variant="outlined" />
{/*Second item */}
<TextField
id="standard-select-currency"
select
label="Price Range"
value={rate}
onChange={e=>setRate(e.target.value)}
variant="standard"
>
{priceRanger.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</TextField>
{/*Third item*/}
<FormControl sx={{ minWidth: 120 }}>
<InputLabel id="demo-simple-select-helper-label">Rate</InputLabel>
<Select
labelId="demo-simple-select-helper-label"
id="demo-simple-select-helper"
value={stars}
label="Rate"
onChange={e=>setStars(e.target.value)} >
<MenuItem value={1}>1</MenuItem>
<MenuItem value={2}>2</MenuItem>
<MenuItem value={3}>3</MenuItem>
<MenuItem value={4}>4</MenuItem>
<MenuItem value={5}>5</MenuItem>
</Select>
</FormControl>
</Box>
);
}
`
I tried to enclose each iteam either in in or in <> but the best I can get is that the last item goes to the next line although not squash, and that's not what I want.

Unable to reduce the dropdown height

I am using "select" dropdown ie "Basic Select" from https://mui.com/components/selects/#basic-select, but the height of the dropdown is more than required,
how to reduce the height of the dropdown?
import * as React from 'react';
import Box from '#mui/material/Box';
import InputLabel from '#mui/material/InputLabel';
import MenuItem from '#mui/material/MenuItem';
import FormControl from '#mui/material/FormControl';
import Select from '#mui/material/Select';
export default function BasicSelect() {
const [age, setAge] = React.useState('');
const handleChange = (event) => {
setAge(event.target.value);
};
return (
<Box sx={{ minWidth: 120 }}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Age</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={age}
label="Age"
onChange={handleChange}
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</Box>
);
}
I have tried passing <FormControl sx={{ m: 1, minWidth: 120, padding: 0 }}> but did not work.
when I change 'padding' in dev tools, height has reduced, but not sure how to apply in this component code.
import * as React from 'react';
import Box from '#mui/material/Box';
import InputLabel from '#mui/material/InputLabel';
import MenuItem from '#mui/material/MenuItem';
import FormControl from '#mui/material/FormControl';
import Select from '#mui/material/Select';
export default function BasicSelect() {
const [age, setAge] = React.useState('');
const handleChange = (event) => {
setAge(event.target.value);
};
return (
<Box sx={{ minWidth: 120 }}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Age</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={age}
label="Age"
onChange={handleChange}
sx={{
"& .MuiSelect-select": {
paddingTop: 0.5,
paddingBottom: 0.5,
},
}}
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</Box>
);
}
Following #JunaidFaryad answer, I got the working code as above.

Showing a default value for a select control using react and material-ui control

I am working on a react page using material-ui. I am retrieving data
from the database to populate my select control.
I want to be able to have a default value like "Select the value". The section highlighted in yellow is just blank.
How can I achieve this? An image of what exist is attached.
<FormControl id="ron" className="form-control">
<InputLabel htmlFor="productDescription" shrink>Product Code/Description</InputLabel>
<Select
value={this.state.productCode}
onChange={this.handleChangeProductCode}
name='productcode'
>
<MenuItem value="">
select the value
</MenuItem>
{this.dataForProductCodeControl()}
</Select>
</FormControl>
dataForProductCodeControl() {
if(this.props.groupedData != undefined){
return this.props.groupedData.map((dt, i) => {
return (
<MenuItem key={i} value={dt.productCode}>
{dt.productCode} | {dt.productDescription}
</MenuItem>
);
});
}
}
You need to specify the displayEmpty prop on the Select.
Here's a working example:
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import InputLabel from "#material-ui/core/InputLabel";
import MenuItem from "#material-ui/core/MenuItem";
import FormControl from "#material-ui/core/FormControl";
import Select from "#material-ui/core/Select";
const useStyles = makeStyles(theme => ({
root: {
display: "flex",
flexWrap: "wrap"
},
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
export default function SimpleSelect() {
const classes = useStyles();
const [values, setValues] = React.useState({
age: ""
});
function handleChange(event) {
setValues(oldValues => ({
...oldValues,
[event.target.name]: event.target.value
}));
}
return (
<form className={classes.root} autoComplete="off">
<FormControl className={classes.formControl}>
<InputLabel htmlFor="age-simple" shrink>
Age
</InputLabel>
<Select
value={values.age}
displayEmpty
onChange={handleChange}
inputProps={{
name: "age",
id: "age-simple"
}}
>
<MenuItem value={""}>Select Age</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</form>
);
}
You can add a disabled valueless item like this:
<MenuItem value="" disabled>
Placeholder
</MenuItem>
But the item will be displayed as an option. See https://material-ui.com/components/selects/#simple-select

Select, OutlineInput label with shrink property not same as TextField when empty

If I use a TextField component, and set InputLabelProps={{shrink: true}}, the Label stays at the top of the TextField, and the outline is cut to show the Label correctly.
However, if I use Select component, as follows :
<FormControl variant={ this.props.variant } className={ classes.formControl } fullWidth>
<InputLabel
ref={ (input) =>{ this.inputLabel = input }}
htmlFor={ this.props.id }
shrink={ true }>
{ this.props.label }
</InputLabel>
<Select
id={ this.props.id }
value={ this.props.value }
onChange={ this.onChange }
input={
<OutlinedInput
labelWidth={ this.state.labelWidth }
name={ this.props.id }
id={ this.props.id }
/>
}
>
{ this.props.options.map(option => (
<MenuItem key={ option.value } value={ option.value }>
<em>{ option.label }</em>
</MenuItem>
))}
</Select>
</FormControl>
The Label stays at the top of the outline just like TextField, however, the outline is not cut out to display the Label nicely, instead it looks like it is "crossed out"
What am I doing wrong?
(Please not, that the this.state.labelWidth has a correct value, even if I hard code this, it still does not work)
Thanks in advance for your help
Cheers
Jason
The default behavior is for the shrink property of InputLabel to be automatically managed by Material-UI. Generally shrink is only applied when the Select has a non-empty value or when it has focus. If you want to have shrink applied all the time, then you also need to specify notched on OutlinedInput since that is what controls leaving a space for the label along the outline.
The code below shows both cases (1. always apply shrink and notched, 2. let Material-UI manage shrink and notched):
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import OutlinedInput from "#material-ui/core/OutlinedInput";
import InputLabel from "#material-ui/core/InputLabel";
import MenuItem from "#material-ui/core/MenuItem";
import FormControl from "#material-ui/core/FormControl";
import Select from "#material-ui/core/Select";
const useStyles = makeStyles(theme => ({
root: {
display: "flex",
flexWrap: "wrap"
},
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
function SimpleSelect() {
const classes = useStyles();
const [values, setValues] = React.useState({
age: ""
});
const inputLabel = React.useRef(null);
const [labelWidth, setLabelWidth] = React.useState(0);
React.useEffect(() => {
setLabelWidth(inputLabel.current.offsetWidth);
}, []);
function handleChange(event) {
setValues(oldValues => ({
...oldValues,
[event.target.name]: event.target.value
}));
}
return (
<form className={classes.root} autoComplete="off">
<FormControl variant="outlined" className={classes.formControl}>
<InputLabel
shrink
ref={inputLabel}
htmlFor="outlined-age-always-notched"
>
Age
</InputLabel>
<Select
value={values.age}
onChange={handleChange}
input={
<OutlinedInput
notched
labelWidth={labelWidth}
name="age"
id="outlined-age-always-notched"
/>
}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
<FormControl variant="outlined" className={classes.formControl}>
<InputLabel ref={inputLabel} htmlFor="outlined-age-simple">
Age
</InputLabel>
<Select
value={values.age}
onChange={handleChange}
input={
<OutlinedInput
labelWidth={labelWidth}
name="age"
id="outlined-age-simple"
/>
}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</form>
);
}
export default SimpleSelect;
Last answer has a deprecated prop labelWidth
For the newer MUI just add shrink prop to InputLabel component and notched prop to Select component.
More on the issue and solution here. https://github.com/mui/material-ui/issues/22799

Open Select after a click on label

I'm looking for a way to open a Select (not native) component from Material UI after a click on the label with htmlFor specified.
<label htmlFor='menu'>Label to open Menu</label>
<MUISelect inputProps={{id: 'menu'}} native={false}>{options}</MUISelect>
Obviously it doesn't work
There are two main aspects to this problem:
You need to get the id onto the same element that has the click-handler for opening the menu.
The element with the click-handler is a div and not an input. Labels do not automatically trigger the click event of the labelled element when it is not an input element.
The first aspect can be solved by using SelectDisplayProps:
SelectDisplayProps={{ id: "menu" }}
The second aspect can be solved with a custom label component:
import React from "react";
const LabelForClickableDiv = ({ htmlFor, ...other }) => {
const onClick = () => {
document.getElementById(htmlFor).click();
};
return <label htmlFor={htmlFor} onClick={onClick} {...other} />;
};
export default LabelForClickableDiv;
Here's a working example:
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import MenuItem from "#material-ui/core/MenuItem";
import FormControl from "#material-ui/core/FormControl";
import Select from "#material-ui/core/Select";
import LabelForClickableDiv from "./LabelForClickableDiv";
const useStyles = makeStyles(theme => ({
root: {
display: "flex",
flexWrap: "wrap"
},
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
function SimpleSelect() {
const classes = useStyles();
const [value, setValue] = React.useState("");
return (
<>
<form className={classes.root} autoComplete="off">
<FormControl className={classes.formControl}>
<LabelForClickableDiv htmlFor="menu">
Label to open Menu
</LabelForClickableDiv>
<Select
value={value}
onChange={event => {
setValue(event.target.value);
}}
SelectDisplayProps={{
id: "menu"
}}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</form>
</>
);
}
export default SimpleSelect;

Resources