Reputation: 117
I'm using Materal-UI's Tabs and I'm facing a problem where every time I click on a tab, it doesn't start executing the functions from the start. Instead all the functions are done in all the tabs at the same time which is not the required thing I need. How can I change it so that when I click on that tab it starts executing the functions inside from the start?
I have made an example of what I mean in the codesandbox below showing 3 tabs with a timer of 5 secs, where all the functions in the tab are executed at the same time which is not what is desired and clicking on the tab doesn't restart it
CodeSandBox Code: https://codesandbox.io/s/material-demo-forked-18xwt
function TabPanel(props) {
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 p={3}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
TabPanel.propTypes = {
children: PropTypes.node,
index: PropTypes.any.isRequired,
value: PropTypes.any.isRequired
};
function a11yProps(index) {
return {
id: `simple-tab-${index}`,
"aria-controls": `simple-tabpanel-${index}`
};
}
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
backgroundColor: theme.palette.background.paper
}
}));
export default function SimpleTabs() {
const classes = useStyles();
const [value, setValue] = React.useState(0);
const [text1, setText1] = React.useState("Wait 1");
const [text2, setText2] = React.useState("Wait 2");
const [text3, setText3] = React.useState("Wait 3");
const handleChange = (event, newValue) => {
setValue(newValue);
};
//
function tab1() {
setTimeout(function () {
setText1("Done 1");
}, 5000);
}
//
function tab2() {
setTimeout(function () {
setText2("Done 2");
}, 5000);
}
//
function tab3() {
setTimeout(function () {
setText3("Done 3");
}, 5000);
}
return (
<div className={classes.root}>
<AppBar position="static">
<Tabs
value={value}
onChange={handleChange}
aria-label="simple tabs example"
>
<Tab label="Item One" {...a11yProps(0)} />
<Tab label="Item Two" {...a11yProps(1)} />
<Tab label="Item Three" {...a11yProps(2)} />
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
{text1}
{tab1()}
</TabPanel>
<TabPanel value={value} index={1}>
{text2}
{tab2()}
</TabPanel>
<TabPanel value={value} index={2}>
{text3}
{tab3()}
</TabPanel>
</div>
);
}
Upvotes: 1
Views: 88
Reputation: 202751
useEffect
hook to "restart" callbacks.Updated Component
export default function SimpleTabs() {
const classes = useStyles();
const [value, setValue] = useState(0);
const [text1, setText1] = useState("Wait 1");
const [text2, setText2] = useState("Wait 2");
const [text3, setText3] = useState("Wait 3");
const timer1Ref = useRef();
const timer2Ref = useRef();
const timer3Ref = useRef();
const handleChange = (event, newValue) => {
setValue(newValue);
};
useEffect(() => {
switch (value) { // <-- invoke specific callback associated with tab
case 0:
tab1();
break;
case 1:
tab2();
break;
case 2:
tab3();
break;
default:
// ignore
}
}, [value]); // <-- trigger when tab index value updates
function tab1() {
clearTimeout(timer1Ref.current);
setText1("Wait 1"); // <-- reset state
timer1Ref.current = setTimeout(function () {
setText1("Done 1");
}, 5000);
}
function tab2() {
clearTimeout(timer2Ref.current);
setText2("Wait 2");
timer2Ref.current = setTimeout(function () {
setText2("Done 2");
}, 5000);
}
function tab3() {
clearTimeout(timer3Ref.current);
setText3("Wait 3");
timer3Ref.current = setTimeout(function () {
setText3("Done 3");
}, 5000);
}
return (
<div className={classes.root}>
<AppBar position="static">
<Tabs
value={value}
onChange={handleChange}
aria-label="simple tabs example"
>
<Tab label="Item One" {...a11yProps(0)} />
<Tab label="Item Two" {...a11yProps(1)} />
<Tab label="Item Three" {...a11yProps(2)} />
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
{text1}
{/* {tab1()} */} // <-- Don't update state from return!!
</TabPanel>
<TabPanel value={value} index={1}>
{text2}
{/* {tab2()} */}
</TabPanel>
<TabPanel value={value} index={2}>
{text3}
{/* {tab3()} */}
</TabPanel>
</div>
);
}
Note: This is a lot of really repetitive code, i.e. not very DRY. Try to abstract the common behavior into a single function.
Upvotes: 2