React Semantic UI - How to disable a child component based on parent - reactjs

How does one disable the input box here, depending on whether a checkbox has been checked or not?
I have verified the checked property - it is in working order (shows true or false boolean values).
However, I can't seem to get the disabled property in Input to work.
Below is my code:
import React from 'react';
import { Button, Checkbox, Input, List } from 'semantic-ui-react'
import PropTypes from 'prop-types';
const CategoryCheckBox = ({ type = 'checkbox', name, mylabel, checked, onChange }) => (
<React.Fragment>
<List horizontal relaxed>
<List.Item>
<Checkbox style={{ paddingLeft:"1em", paddingBottom: "1em" }} label={mylabel} checked={checked} onChange={onChange} />
<List.Content>
<Input style={{ maxWidth:"9em", paddingLeft:"1em" }} size='mini' focus placeholder='Min' disabled={checked === true ? true : false} />
<Input style={{ maxWidth:"9em", paddingLeft:"1em" }} size='mini' focus placeholder='Max' />
</List.Content>
</List.Item>
</List>
</React.Fragment>
);
CategoryCheckBox.propTypes = {
type: PropTypes.string,
name: PropTypes.string.isRequired,
mylabel: PropTypes.string.isRequired,
checked: PropTypes.bool,
onChange: PropTypes.func.isRequired,
}
export default CategoryCheckBox;
From the main program,
The component is called with the below parameters:
<CategoryCheckBox name={item.value} mylabel={item.text} checked={this.state.checkedItems.get(item.value)} onChange={this.handleChange} />
Below is my screenshot for the component along with React debugger showing the checked value.
Any Help will be highly appreciated.
Tried to set up most of the important code - Newbie to React myself. Can't get the index.js in the working order. But it gives you a good idea of my code https://codesandbox.io/embed/2pyoxpr6rn?fontsize=14

Changes:
1- You are not assigning the name to CheckBox element in CategoryCheckBox, add name here:
<Checkbox
style={{ paddingLeft: "1em", paddingBottom: "1em" }}
label={mylabel}
// ↓↓↓↓↓↓↓↓ here
name={name}
checked={checked}
onChange={onChange}
/>
2- Add disable condition for Max input field also:
<Input
style={{ maxWidth: "9em", paddingLeft: "1em" }}
size="mini"
focus
placeholder="Max"
// ↓↓↓↓↓↓↓↓ here
disabled={!checked}
/>
3- Most importantly, You are storing data in states in App component so it needs to be a class component.
Check Working Codesandbox with all these changes.

After looking at your code the problem is with your app component. Your app component has a state and therefore cannot be a stateless functional component. Your App component should look something like this.
import React from "react";
import ReactDOM from "react-dom";
import { Checkbox, Label, Header, Card, Form, Button, Container, Icon, Step, Select, Dropdown, List } from 'semantic-ui-react';
import womensBoutiqueOptions from "./womensBoutiqueOptions";
import CategoryCheckBox from "./CategoryCheckBox";
import "./styles.css";
class App() extends React.Component {
state = {
date: new Date(),
time: '10:00',
checkedItems: new Map()
}
handleChange(e) {
const item = e.target.name;
const isChecked = e.target.checked;
this.setState(prevState => ({ checkedItems: prevState.checkedItems.set(item, isChecked) }));
}
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Form size="large" key="large">
<h4 className="ui dividing header">Womens Information</h4>
<Form.Group widths='equal'>
<Form.Field>
<React.Fragment>
<List horizontal >
{
womensBoutiqueOptions.map(item => (
<List.Item key={item.key}>
<List.Content>
<CategoryCheckBox name={item.value} mylabel={item.text} checked={this.state.checkedItems.get(item.value)} onChange={this.handleChange} />
</List.Content>
</List.Item>
))
}
</List>
</React.Fragment>
</Form.Field>
</Form.Group>
</Form>
</div>
)
}
);
}
Check out the react documentation for some more information on why

Related

The problem with displaying imask with material ui TextField

If I enter One character and remove the focus, the Textfield breaks.
I guess mask problems
As shown in the picture
My code https://codesandbox.io/s/happy-blackwell-5sigw?file=/src/App.js:0-610
import { TextField } from "#material-ui/core";
import { IMaskMixin } from "react-imask";
import { useForm } from "react-hook-form";
const IMaskPhoneInput = IMaskMixin(({ ...props }) => {
return <TextField {...props} />;
});
export default function App() {
const {
register,
} = useForm();
return (
<div className="App">
<IMaskPhoneInput
autoFocus
fullWidth
mask={"+{7} (000) 000-00-00"}
color="primary"
label={"Телефон"}
placeholder={"+7 (950) 356-55-44"}
{...register("phone")}
/>
</div>
);
}
Have you tried including the shrink prop?
<TextField InputLabelProps={{ shrink: true }} />
This is per the documentation at https://mui.com/components/text-fields/ under limitations
Shrink
The input label "shrink" state isn't always correct. The input label is supposed to shrink as soon as the input is displaying something. In some circumstances, we can't determine the "shrink" state (number input, datetime input, Stripe input). You might notice an overlap.
To workaround the issue, you can force the "shrink" state of the label.
<TextField InputLabelProps={{ shrink: true }} /> or <InputLabel shrink>Count</InputLabel>

Stop Material-UI's Select Popover from changing position [duplicate]

MultiSelect box popover keeps jumping when scroll or select items
Codepen https://codesandbox.io/s/material-demo-e5j8h
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Input from "#material-ui/core/Input";
import InputLabel from "#material-ui/core/InputLabel";
import MenuItem from "#material-ui/core/MenuItem";
import FormControl from "#material-ui/core/FormControl";
import ListItemText from "#material-ui/core/ListItemText";
import Select from "#material-ui/core/Select";
import Checkbox from "#material-ui/core/Checkbox";
const useStyles = makeStyles(theme => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
maxWidth: 300
},
chips: {
display: "flex",
flexWrap: "wrap"
},
chip: {
margin: 2
},
noLabel: {
marginTop: theme.spacing(3)
}
}));
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250
}
}
};
const names = [
"Oliver Hansen",
"Van Henry",
"April Tucker",
"Ralph Hubbard",
"Omar Alexander",
"Carlos Abbott",
"Miriam Wagner",
"Bradley Wilkerson",
"Virginia Andrews",
"Kelly Snyder"
];
export default function MultipleSelect() {
const classes = useStyles();
const [personName, setPersonName] = React.useState([]);
const handleChange = event => {
setPersonName(event.target.value);
};
return (
<div>
long text <br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
<FormControl className={classes.formControl}>
<InputLabel id="demo-mutiple-checkbox-label">Tag</InputLabel>
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
onChange={handleChange}
input={<Input />}
renderValue={selected => selected.join(", ")}
MenuProps={MenuProps}
>
{names.map(name => (
<MenuItem key={name} value={name}>
<Checkbox checked={personName.indexOf(name) > -1} />
<ListItemText primary={name} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
The cause of the jumping is related to the "content anchor" functionality of the Menu.
From https://material-ui.com/components/menus/#selected-menus (emphasis mine):
If used for item selection, when opened, simple menus attempt to vertically align the currently selected menu item with the anchor element, and the initial focus will be placed on the selected menu item. The currently selected menu item is set using the selected property (from ListItem). To use a selected menu item without impacting the initial focus or the vertical positioning of the menu, set the variant property to menu.
You can also find a similar note in the documentation of the variant prop.
The other relevant portion of the documentation is the description of the getContentAnchorEl prop of Popover:
This function is called in order to retrieve the content anchor element. It's the opposite of the anchorEl prop. The content anchor element should be an element inside the popover. It's used to correctly scroll and set the position of the popover. The positioning strategy tries to make the content anchor element just above the anchor element.
The default behavior of the Select element is to use the Select input element (the part that shows the selected item(s) when closed) as the "anchor element" and the last selected menu item as the "content anchor element". This means that when the Popover is open, it tries to align the last selected menu item (within the Popover) with the Select input element (behind the Popover).
In the case of using the multiple property on the Select, you have the potential to change the last selected item while the Popover is open (for single select, it would typically close immediately after selecting something). Additionally, since not all the menu items fit at once, the last selected item is sometimes scrolled out of view which forces the Popover to use slightly different logic for the vertical alignment.
The net effect of all this is the weird jumping demonstrated in your sandbox. You can fix this by forcing Popover to use a contentAnchorOffset of zero by specifying getContentAnchorEl: null as follows:
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250
}
},
getContentAnchorEl: null
};
You may also want to add variant: "menu" to get rid of some auto focus behavior which will cause it to automatically scroll to the last selected item. This is nice behavior for single select, but less useful and somewhat disorienting in a multi-select.
Setting variant: "menu" is not sufficient (without getContentAnchorEl: null) to get rid of the jumping. This would cause it to always use the first menu item as the content anchor which would result in less jumping, but it would still do some jumping due to the first menu item sometimes being scrolled out of view when changing selections.
Below is the full code for the modified version of your sandbox that no longer has any weird jumping (the only changes are to the MenuProps):
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Input from "#material-ui/core/Input";
import InputLabel from "#material-ui/core/InputLabel";
import MenuItem from "#material-ui/core/MenuItem";
import FormControl from "#material-ui/core/FormControl";
import ListItemText from "#material-ui/core/ListItemText";
import Select from "#material-ui/core/Select";
import Checkbox from "#material-ui/core/Checkbox";
const useStyles = makeStyles(theme => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
maxWidth: 300
},
chips: {
display: "flex",
flexWrap: "wrap"
},
chip: {
margin: 2
},
noLabel: {
marginTop: theme.spacing(3)
}
}));
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250
}
},
variant: "menu",
getContentAnchorEl: null
};
const names = [
"Oliver Hansen",
"Van Henry",
"April Tucker",
"Ralph Hubbard",
"Omar Alexander",
"Carlos Abbott",
"Miriam Wagner",
"Bradley Wilkerson",
"Virginia Andrews",
"Kelly Snyder"
];
export default function MultipleSelect() {
const classes = useStyles();
const [personName, setPersonName] = React.useState([]);
const handleChange = event => {
setPersonName(event.target.value);
};
return (
<div>
long text <br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
<FormControl className={classes.formControl}>
<InputLabel id="demo-mutiple-checkbox-label">Tag</InputLabel>
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
onChange={handleChange}
input={<Input />}
renderValue={selected => selected.join(", ")}
MenuProps={MenuProps}
>
{names.map(name => (
<MenuItem key={name} value={name}>
<Checkbox checked={personName.indexOf(name) > -1} />
<ListItemText primary={name} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
First, thank you Ryan Cogswell, for great explanation on why this is happening and also how to solve it. I was trying to solve the issue of Select jumping during multiple selection, and was able to implement fix thanks to your answer. One thing I wanted to add was for other developers using typescript like myself, if you implement the above solution directly you will run into
" Type '{ PaperProps: { style: { float: string; minWidth: number; display: string; flexWrap: string; flexDirection: string; }; }; variant: string; getContentAnchorEl: null; }' is not assignable to type 'Partial'.
Types of property 'variant' are incompatible.
Type 'string' is not assignable to type '"menu" | "selectedMenu" | undefined'. TS2322 "
if you are having this type compatibility issue, declaring the MenuProps directly like below will fix it.
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
onChange={handleChange}
input={<Input />}
renderValue={selected => selected.join(", ")}
MenuProps={{
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250
}
},
variant: "menu",
getContentAnchorEl: null
}}>
This, worked for my project, but please let me know if there are better solutions to this type compatibility issue.

MultiSelect box popover keeps jumping when scroll or select items

MultiSelect box popover keeps jumping when scroll or select items
Codepen https://codesandbox.io/s/material-demo-e5j8h
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Input from "#material-ui/core/Input";
import InputLabel from "#material-ui/core/InputLabel";
import MenuItem from "#material-ui/core/MenuItem";
import FormControl from "#material-ui/core/FormControl";
import ListItemText from "#material-ui/core/ListItemText";
import Select from "#material-ui/core/Select";
import Checkbox from "#material-ui/core/Checkbox";
const useStyles = makeStyles(theme => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
maxWidth: 300
},
chips: {
display: "flex",
flexWrap: "wrap"
},
chip: {
margin: 2
},
noLabel: {
marginTop: theme.spacing(3)
}
}));
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250
}
}
};
const names = [
"Oliver Hansen",
"Van Henry",
"April Tucker",
"Ralph Hubbard",
"Omar Alexander",
"Carlos Abbott",
"Miriam Wagner",
"Bradley Wilkerson",
"Virginia Andrews",
"Kelly Snyder"
];
export default function MultipleSelect() {
const classes = useStyles();
const [personName, setPersonName] = React.useState([]);
const handleChange = event => {
setPersonName(event.target.value);
};
return (
<div>
long text <br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
<FormControl className={classes.formControl}>
<InputLabel id="demo-mutiple-checkbox-label">Tag</InputLabel>
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
onChange={handleChange}
input={<Input />}
renderValue={selected => selected.join(", ")}
MenuProps={MenuProps}
>
{names.map(name => (
<MenuItem key={name} value={name}>
<Checkbox checked={personName.indexOf(name) > -1} />
<ListItemText primary={name} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
The cause of the jumping is related to the "content anchor" functionality of the Menu.
From https://material-ui.com/components/menus/#selected-menus (emphasis mine):
If used for item selection, when opened, simple menus attempt to vertically align the currently selected menu item with the anchor element, and the initial focus will be placed on the selected menu item. The currently selected menu item is set using the selected property (from ListItem). To use a selected menu item without impacting the initial focus or the vertical positioning of the menu, set the variant property to menu.
You can also find a similar note in the documentation of the variant prop.
The other relevant portion of the documentation is the description of the getContentAnchorEl prop of Popover:
This function is called in order to retrieve the content anchor element. It's the opposite of the anchorEl prop. The content anchor element should be an element inside the popover. It's used to correctly scroll and set the position of the popover. The positioning strategy tries to make the content anchor element just above the anchor element.
The default behavior of the Select element is to use the Select input element (the part that shows the selected item(s) when closed) as the "anchor element" and the last selected menu item as the "content anchor element". This means that when the Popover is open, it tries to align the last selected menu item (within the Popover) with the Select input element (behind the Popover).
In the case of using the multiple property on the Select, you have the potential to change the last selected item while the Popover is open (for single select, it would typically close immediately after selecting something). Additionally, since not all the menu items fit at once, the last selected item is sometimes scrolled out of view which forces the Popover to use slightly different logic for the vertical alignment.
The net effect of all this is the weird jumping demonstrated in your sandbox. You can fix this by forcing Popover to use a contentAnchorOffset of zero by specifying getContentAnchorEl: null as follows:
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250
}
},
getContentAnchorEl: null
};
You may also want to add variant: "menu" to get rid of some auto focus behavior which will cause it to automatically scroll to the last selected item. This is nice behavior for single select, but less useful and somewhat disorienting in a multi-select.
Setting variant: "menu" is not sufficient (without getContentAnchorEl: null) to get rid of the jumping. This would cause it to always use the first menu item as the content anchor which would result in less jumping, but it would still do some jumping due to the first menu item sometimes being scrolled out of view when changing selections.
Below is the full code for the modified version of your sandbox that no longer has any weird jumping (the only changes are to the MenuProps):
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Input from "#material-ui/core/Input";
import InputLabel from "#material-ui/core/InputLabel";
import MenuItem from "#material-ui/core/MenuItem";
import FormControl from "#material-ui/core/FormControl";
import ListItemText from "#material-ui/core/ListItemText";
import Select from "#material-ui/core/Select";
import Checkbox from "#material-ui/core/Checkbox";
const useStyles = makeStyles(theme => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120,
maxWidth: 300
},
chips: {
display: "flex",
flexWrap: "wrap"
},
chip: {
margin: 2
},
noLabel: {
marginTop: theme.spacing(3)
}
}));
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250
}
},
variant: "menu",
getContentAnchorEl: null
};
const names = [
"Oliver Hansen",
"Van Henry",
"April Tucker",
"Ralph Hubbard",
"Omar Alexander",
"Carlos Abbott",
"Miriam Wagner",
"Bradley Wilkerson",
"Virginia Andrews",
"Kelly Snyder"
];
export default function MultipleSelect() {
const classes = useStyles();
const [personName, setPersonName] = React.useState([]);
const handleChange = event => {
setPersonName(event.target.value);
};
return (
<div>
long text <br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
long text
<br />
<FormControl className={classes.formControl}>
<InputLabel id="demo-mutiple-checkbox-label">Tag</InputLabel>
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
onChange={handleChange}
input={<Input />}
renderValue={selected => selected.join(", ")}
MenuProps={MenuProps}
>
{names.map(name => (
<MenuItem key={name} value={name}>
<Checkbox checked={personName.indexOf(name) > -1} />
<ListItemText primary={name} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
First, thank you Ryan Cogswell, for great explanation on why this is happening and also how to solve it. I was trying to solve the issue of Select jumping during multiple selection, and was able to implement fix thanks to your answer. One thing I wanted to add was for other developers using typescript like myself, if you implement the above solution directly you will run into
" Type '{ PaperProps: { style: { float: string; minWidth: number; display: string; flexWrap: string; flexDirection: string; }; }; variant: string; getContentAnchorEl: null; }' is not assignable to type 'Partial'.
Types of property 'variant' are incompatible.
Type 'string' is not assignable to type '"menu" | "selectedMenu" | undefined'. TS2322 "
if you are having this type compatibility issue, declaring the MenuProps directly like below will fix it.
<Select
labelId="demo-mutiple-checkbox-label"
id="demo-mutiple-checkbox"
multiple
value={personName}
onChange={handleChange}
input={<Input />}
renderValue={selected => selected.join(", ")}
MenuProps={{
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250
}
},
variant: "menu",
getContentAnchorEl: null
}}>
This, worked for my project, but please let me know if there are better solutions to this type compatibility issue.

How to pass a function as props to a component of Material-UI for React?

I wanted to make an Appbar which will contain a SearchBar. for this I was using Material-UI for React.
I have a file in my component section which loads the Appbar
Here is the appbar.js
function SearchAppBar(props) {
const { classes, placeholder, writeInput } = props;
return (
<div className={classes.root}>
<AppBar position="fixed">
<Toolbar>
<IconButton
className={classes.menuButton}
color="inherit"
aria-label="Open drawer"
>
<MenuIcon />
</IconButton>
<Typography
className={classes.title}
variant="h6"
color="inherit"
noWrap
>
My Weather
</Typography>
<div className={classes.grow} />
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder={placeholder}
classes={{
root: classes.inputRoot,
input: classes.inputInput
}}
onChange={this.writeInput}
/>
</div>
</Toolbar>
</AppBar>
</div>
);
}
SearchAppBar.propTypes = {
classes: PropTypes.object.isRequired,
placeholder: PropTypes.string,
writeInput: PropTypes.func
};
export default withStyles(style_appbar)(SearchAppBar);
After that, I imported this file to another file named searchbar.js.
Why I did that?
Well, so that I can control the input with the component state and the props
Here is the searchcbar.js
import React, { Component } from "react";
import AppBar from "./appbar";
export default class Search extends Component {
constructor(props) {
super(props);
this.state = { term: "" };
this.onInputChange = this.onInputChange.bind(this);
}
onInputChange(event) {
console.log(event.target.value);
this.setState({ term: event.target.value });
}
render() {
return (
<AppBar
placeholder="Search forecast for your favorite cities..."
value={this.state.term}
writeInput={this.onInputChange}
/>
);
}
}
And then I imported this file to my app.js.
Before diving deep, I wanted to see if everything works, so I put a console log but I am not getting any console log.
Rather I am having the error,
Cannot read property 'writeInput' of undefined in appbar.js:46:29
writeInput is the function which I was sending to the Materila-UI's component as a props!
The link of the sandbox of the code is here, you can check the output too.project's sandbox
ps. It maye take some time for the sandbox to boot up!
So, can I not send a function as props to the Material-UI's component? If not what's the alternate way to resolve this issue?
You can send a function to functional component and access it. Don’t use this when calling writeInput. Since you are assigning const { writeInput} = props; you have to call writeInput or if you don’t assign like this then you can call props.writeInput
PFB corrected code
<InputBase
placeholder={placeholder}
classes={{
root: classes.inputRoot,
input: classes.inputInput
}}
onChange={writeInput}
/>

React Dropbox Component using Material-UI-Next

Is there a way to nest a dropdown component with MenuItem? I would like to declare one component whenever I need to use a drop down instead of nesting two.
Am I on the right path or should drop down components be handled differently than normal React components?
App.js (this currently works nesting MenuItem within DropDown)
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
>
{stateAbbreviationData.map(option => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</DropDown>;
App.js (how I would like to use my component, does not work)
Shows code (data.map(option => ( ))} ) instead of a list when clicked. Further code below.
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
menuData="stateAbbreviationData"
menuKey={stateAbbreviationData.value}
menuValue={stateAbbreviationData.value}
menuOptionLabel={stateAbbreviationData.label}
/>;
DropDown.js
import React from "react";
import TextField from "material-ui/TextField";
import DropDownMenu from "./DropDownMenu";
const styles = {
minWidth: 175
};
class DropDown extends React.Component {
render() {
return (
<TextField
select
label={this.props.label}
value={this.props.value}
style={styles}
onChange={this.props.onChange}
margin="normal"
InputLabelProps={{
shrink: true
}}
menuData={this.props.menuData}
>
<DropDownMenu
key={this.props.menuKey}
value={this.props.menuValue}
optionLabel={this.props.menuOptionLabel}
/>
</TextField>
);
}
}
export default DropDown;
DropDownMenu.js
import React from "react";
import MenuItem from "material-ui/Menu/MenuItem";
const styles = {
minWidth: 175
};
class DropDownMenu extends React.Component {
render() {
return (
<div>
{this.props.menuData.map(option => (
<MenuItem key={this.props.key} value={this.props.value}>
{this.props.optionLabel}
</MenuItem>
))}
</div>
);
}
}
export default DropDownMenu;
StateAbbreviationData.js
const stateAbbreviationData = [
{
value: "AL",
label: "AL"
},
{
value: "AK",
label: "AK"
}
// ...
];
export default stateAbbreviationData;
I think your first example is the one that you want to create with your DropDown component. Try removing DropDownMenu component, and doing all of the logic inside DropDown:
import React from "react";
import TextField from "material-ui/TextField";
import MenuItem from "material-ui/Menu/MenuItem";
const styles = {
minWidth: 175
};
class DropDown extends React.Component {
render() {
return (
<TextField
select
label={this.props.label}
value={this.props.value}
style={styles}
onChange={this.props.onChange}
margin="normal"
InputLabelProps={{
shrink: true
}}
>
{this.props.menuData.map(option => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</TextField>
);
}
}
export default DropDown;
And then you can use it the way you want to:
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
menuData={stateAbbreviationData} // <-- Put in braces
/>
Note:
With Material UI, I've used MenuItem components inside of the Select component, rather than the TextField component like you're doing:
https://material-ui.com/demos/selects/
There were a couple issues with your existing code I'll point out.
As I indicated above, you need to pass the data stateAbbreviationData into the DropDown component's props using curly braces:
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
menuData={stateAbbreviationData} // <-- This change
menuKey={stateAbbreviationData.value}
menuValue={stateAbbreviationData.value}
menuOptionLabel={stateAbbreviationData.label}
/>
There's another bug with the way you're setting the menuKey, menuValue, and manuOptionLabel props. With the code you included, it seems like stateAbbreviationData is an array of objects.
In the props, you're trying to access properties on that array that don't exist: stateAbbreviationData.value and stateAbbreviationData.label. The objects in the array have those bits, but the array itself doesn't.
<DropDown
label="Drop Down Label"
value={this.state.selectedState}
onChange={this.handleChange}
menuData={stateAbbreviationData}
menuKey={stateAbbreviationData.value} // <-- Doesn't exist
menuValue={stateAbbreviationData.value} // <-- Doesn't exist
menuOptionLabel={stateAbbreviationData.label} // <-- Doesn't exist
/>
There's also a bug where you're passing menuData to the TextField component, but instead you should pass it to DropDownMenu.
That said, I recommend going with the first solution I proposed. Good luck!

Resources