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
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 this example code from MUI Tabs (https://mui.com/material-ui/react-tabs/#BasicTabs.tsx). The full code is below.
My question is, what is the newValue in this code? Which value is it reading and where did it come from? It throws an error without event so it seems like it's linked but I can't get a full understanding of this part.
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
setValue(newValue);
};
FULL CODE
import * as React from 'react';
import Tabs from '#mui/material/Tabs';
import Tab from '#mui/material/Tab';
import Typography from '#mui/material/Typography';
import Box from '#mui/material/Box';
interface TabPanelProps {
children?: React.ReactNode;
index: number;
value: number;
}
function TabPanel(props: TabPanelProps) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && (
<Box sx={{ p: 3 }}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
export default function BasicTabs() {
const [value, setValue] = React.useState(0);
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
setValue(newValue);
};
return (
<Box sx={{ width: '100%' }}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs value={value} onChange={handleChange} aria-label="basic tabs example">
<Tab label="Item One" {...a11yProps(0)} />
<Tab label="Item Two" {...a11yProps(1)} />
<Tab label="Item Three" {...a11yProps(2)} />
</Tabs>
</Box>
<TabPanel value={value} index={0}>
Item One
</TabPanel>
<TabPanel value={value} index={1}>
Item Two
</TabPanel>
<TabPanel value={value} index={2}>
Item Three
</TabPanel>
</Box>
);
}
Basically the newValue its related to the tab index.
So when user changes a tab, the MUI code internally handles the onChange event call passing the event itself and the newValue (with the tab index clicked). And with it MUI identifies wich tab user have clicked and change it to take effect into screen.
You can check the definition here.
What I'm trying to do is get which is the key of the sider the user is using (without ReactDOM and with functional components)
my sample code here:
export default function DrawerSider() {
const history = useHistory();
const [selectedKey, setSelectedKey] = useState("sub1")
const handleSelectKey = function(key, history_string) {
setSelectedKey(key)
history.push(history_string)
console.log(key)
}
return (
<Sider width={200} className="site-layout-background">
<Menu
defaultSelectedKeys={selectedKey}
mode="inline"
style={{ height: "100%", borderRight: 0 }}
>
<Menu.Item
key="sub1"
icon={<HomeOutlined />}
onClick={() => handleSelectKey("sub1","/dashboard/resumo")}
>
Dashboard
</Menu.Item>
<SubMenu key="sub2" icon={<UserOutlined />} title="Usuários">
<Menu.Item
key="1"
icon={<PlusCircleOutlined />}
onClick={() => handleSelectKey("1","/usuarios/novo")}
>
Adicionar usúario
</Menu.Item>
<Menu.Item
key="2"
icon={<TableOutlined />}
onClick={() => handleSelectKey("2","/usuarios/todos")}
>
Todos usúarios
</Menu.Item>
</SubMenu>
<SubMenu key="sub3" icon={<FormOutlined />} title="Formulários">
<Menu.Item
key="3"
icon={<PlusCircleOutlined />}
onClick={() => handleSelectKey("3","/formularios/novo")}
>
Adicionar formulário
</Menu.Item>
</SubMenu>
</SubMenu>
</Menu>
</Sider>
);
}
Obs: I'm using ant design lib
Someone knows how to make it works?
I tried to use a const on click event to set a state of the selected key , but it didn't work
There is no onClick on Menu.Item
only Menu component has onClick or onSelect, both will give you callback of the "clicked" key, the differences is onClick is for any menu item click (including expanding menu) while on select is when you select an actual menu item.
There are 2 ways to get the history string:
1 - use the history string as menuItem key:
the issue would be you cannot have 2 menu item that has same history key
2 - have a map which maps the menu key to history string
see below demo:
https://codesandbox.io/s/inline-menu-antd-4-18-2-forked-bgwyj?file=/index.js
const handleOnSelect = ({ item, key, keyPath, selectedKeys, domEvent }) => {
console.log(item);
console.log(keyPath);
console.log(selectedKeys);
console.log(domEvent);
handleSelectKey(key);
};
...
..
<Menu
onSelect={handleOnSelect}
...
>
...
</Menu>
You need to save the selected key as part of the component's state. You can set the current selected key as part of the onClick.
const selectedKey, setSelectedKey = useState("defaultSelectedKey")
Not sure what the useHistory hook is for but if you need to set that onClick as well as the selectedKey, move it all into one function like so
const handleSelectKey = function(key, history_string) {
setSelectedKey(key)
history.push(history_string)
}
...
onclick={() => handleSelectedKey("3", "/usuarios/novo")}
https://ant.design/components/menu/
EDIT
Based on research into your design library, your onclick and handle select must be at the top level of the menu component. Just copy the syntax that they use in their example code.
https://ant.design/components/menu/
I want to build simple form using Material-UI which has:
multiple options, checkboxes
only one option can be true, checked
And then, basing on which option is checked I want to push certain information to my formData and send it to my backend on handleSubmit
My Form component looks like this:
const MyFormComponent= () => {
const [formData, setFormData] = useState({ reason: "", feedback: ""});
const [checkboxesState, setCheckboxesState] = useState({
one: false,
two: false,
three: false
})
const handleSubmit = (e) => {
/* ... */
};
const handleChange = (e) => {
/* ... */
}
return (
<>
<form onSubmit={handleSubmit}>
<Grid container>
<FormLabel component="legend"> Some asked question here? </FormLabel>
<FormGroup>
<FormControlLabel
control={<Checkbox checked={checkboxesState.one} color="primary" onChange={handleChange} name="one" />}
label="Label for my first checkbox"
/>
<FormControlLabel
control={<Checkbox checked={checkboxesState.two} color="primary" onChange={handleChange} name="two" />}
label="Label for my secound checkbox"
/>
<FormControlLabel
control={<Checkbox checked={checkboxesState.three} color="primary" onChange={handleChange} name="three" />}
label="Label for my third checkbox"
/>
</FormGroup>
</Grid>
<ButtonWrapper>
<Button variant="outlined" color="secondary" type="submit"> Delete </Button>
<Button variant="contained" color="primary"> Go back </Button>
</ButtonWrapper>
</form>
</>
);
}
So I came up with solution on my handleChange like this, works perfectly fine, but I will have to create another if(/* */) else if(/* */) else if(/* */) instruction on my handleSubmit.
I believe there is much more elegant solution based on Material-UI components API which I don't know yet or don't know how to use it. Can someone suggest me something else in this situation?
const handleChange = (e) => {
if(e.target.name === "one") {
setCheckboxesState({
one: e.target.checked,
two: false,
three: false
});
} else if (e.target.name === "two") {
setCheckboxesState({
one: false,
two: e.target.checked,
three: false
});
} else if (e.target.name === "three") {
setCheckboxesState({
one: false,
two: false,
three: e.target.checked
});
}
}
If you want to achieve "Multiple options, only one selected", Radio Buttons is a better UX choice for that scenario.
Quoting from https://www.nngroup.com/articles/checkboxes-vs-radio-buttons/
Radio buttons are used when there is a list of two or more options
that are mutually exclusive and the user must select exactly one
choice. In other words, clicking a non-selected radio button will
deselect whatever other button was previously selected in the list.
Checkboxes are used when there are lists of options and the user may
select any number of choices, including zero, one, or several. In
other words, each checkbox is independent of all other checkboxes in
the list, so checking one box doesn't uncheck the others.
To achieve Radio group using Material UI, please refer to Material UI docs. They have excellent examples.
import React from 'react';
import Radio from '#material-ui/core/Radio';
import RadioGroup from '#material-ui/core/RadioGroup';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import FormControl from '#material-ui/core/FormControl';
import FormLabel from '#material-ui/core/FormLabel';
export default function RadioButtonsGroup() {
const [value, setValue] = React.useState('female');
const handleChange = (event) => {
setValue(event.target.value);
};
return (
<FormControl component="fieldset">
<FormLabel component="legend">Gender</FormLabel>
<RadioGroup aria-label="gender" name="gender1" value={value} onChange={handleChange}>
<FormControlLabel value="female" control={<Radio />} label="Female" />
<FormControlLabel value="male" control={<Radio />} label="Male" />
<FormControlLabel value="other" control={<Radio />} label="Other" />
<FormControlLabel value="disabled" disabled control={<Radio />} label="(Disabled option)" />
</RadioGroup>
</FormControl>
);
}
You could track the current select checkbox with one variable instead of multiple.
const MyFormComponent= () => {
const [formData, setFormData] = useState({ reason: "", feedback: ""});
const [checkboxesState, setCheckboxesState] = useState(-1)
const handleSubmit = (e) => {
/* ... */
};
const handleChange = (e) => {
setCheckboxesState(e.target.name);
}
return (
<>
<form onSubmit={handleSubmit}>
<Grid container>
<FormLabel component="legend"> Some asked question here? </FormLabel>
<FormGroup>
<FormControlLabel
control={<Checkbox checked={checkboxesState === 0} color="primary" onChange={handleChange} name={0} />}
label="Label for my first checkbox"
/>
<FormControlLabel
control={<Checkbox checked={checkboxesState === 1} color="primary" onChange={handleChange} name={1} />}
label="Label for my secound checkbox"
/>
<FormControlLabel
control={<Checkbox checked={checkboxesState === 2} color="primary" onChange={handleChange} name={2} />}
label="Label for my third checkbox"
/>
</FormGroup>
</Grid>
<ButtonWrapper>
<Button variant="outlined" color="secondary" type="submit"> Delete </Button>
<Button variant="contained" color="primary"> Go back </Button>
</ButtonWrapper>
</form>
</>
);
}
currently I am working on a project with React and Material UI. I want to hover on tabs that will open an menu, but this doesn't really work. I am hoping that you guys can help me (and maybe tell me if I'm approaching this correctly)
Where my tabs are basing of: https://imgur.com/a/HeiL2xo
My current project: https://imgur.com/a/Ik5NEkF
AppBarTop class
class AppBarTop extends Component {
state = {
value: 0,
open: false,
anchorEl: null
};
handleMenuClick = (index) => {
}
handleMenuOpen = (index, event) => {
const {currentTarget} = event;
this.setState({
open: !this.state.open,
anchorEl: currentTarget,
value: index
})
};
handleMenuClose = () => {
this.setState({
open: false,
anchorEl: null,
})
}
handleInputSearch = () => {
};
render() {
const {classes} = this.props;
const {anchorEl, open} = this.state;
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<img src={buddies} alt={"buddies"} height={50} width={50}/>
<div className={classes.grow}/>
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon/>
</div>
<InputBase
placeholder="Search…"
onChange={this.handleInputSearch}
classes={{
root: classes.inputRoot,
input: classes.inputInput
}}
/>
</div>
<div className={classes.grow}/>
<List>
{TopMenu.map((item, index) => (
<Tab key={index} component={Link} to={{pathname: item.pathname}}
classes={{root: classes.tabItem}} label={item.label}/>
))}
</List>
</Toolbar>
<Paper className={classes.grow}>
<Tabs
value={this.state.value}
indicatorColor="primary"
textColor="primary"
centered>
{BottomMenu.map((item, index) => (
<Tab
key={index}
onMouseOver={this.handleMenuOpen.bind(this, index)}
data-key={index}
classes={{root: classes.tabItem}}
label={item.label}
aria-owns={open ? 'menu-list-grow' : undefined}
aria-haspopup={"true"}/>
))}
</Tabs>
<Popper open={open} anchorEl={anchorEl} id="menu-list-grow">
<Paper>
<MenuList>
{BottomMenu[this.state.value].items.map((item, index) => (
<MenuItem key={index} onClick={this.handleMenuClose}>{item}</MenuItem>
))}
</MenuList>
</Paper>
</Popper>
</Paper>
</AppBar>
</div>
);
}
}
export default withStyles(styles)(AppBarTop)
The key problem here is that the onMouseOver event handler is fired multiple times as you move around the <Tab> component. Your handleMenuOpen function is not built to handle this.
I've replicated your issue in a CodeSandbox here: https://codesandbox.io/s/qkw8rr4mk4
The following 3 points will fix your menu issues:
Change handleMenuOpen to be functional by explicitly setting open: true
Use onMouseEnter rather than onMouseOver. This is not required but it makes for more predictable functionality as onMouseEnter is only called once
To automatically close your menu when your mouse leaves them add the onMouseLeave={this.handleMenuClose.bind(this)} property to your parent <div> component
A CodeSandbox with the above 3 points implemented can be found at: https://codesandbox.io/s/6x9w9m6n7r