React Material UI - Tabs - How to disable lazy loading of tabs? - reactjs

I am creating some react tabs based off of the example shown here for Material UI: https://material-ui.com/components/tabs/.
The problem I am encountering is that my tabs are lazy loading the linegraph components instead of performing all the queries on initial page load. Does anyone have some suggestions of where I can optimize this to make each tab load immediately on page load?
function TabPanel(props) {
const { children, value, index} = props;
return (
<Typography>
{value === index && <Box p={3}>{children}</Box>}
</Typography>
);
}
class SimpleTabs extends Component {
constructor(props) {
super(props)
console.log(props.color)
this.state = {
value: 0
};
}
handleChange = (event, newValue) => {
this.setState({value: newValue});
};
render() {
return (
<div>
<AppBar position="static">
<Tabs value={this.state.value} onChange={this.handleChange}>
<Tab label="Item One"/>
<Tab label="Item Two"/>
<Tab label="Item Three"/>
</Tabs>
</AppBar>
<TabPanel value={this.state.value} index={0}>
<LineGraph className={styles.graphBox} name={"Guest Count"} url={'http://127.0.0.1:5000/***/***'} initialFilterData={this.props.initialFilterData} filterData={this.props.filterData}/>
</TabPanel>
<TabPanel value={this.state.value} index={1}>
<LineGraph className={styles.graphBox} name={"Total Sales"} url={'http://127.0.0.1:5000/***/***'} initialFilterData={this.props.initialFilterData} filterData={this.props.filterData}/>
</TabPanel>
<TabPanel value={this.state.value} index={2}>
<LineGraph className={styles.graphBox} name={"Avg Check Size"} url={'http://127.0.0.1:5000/***/***'} initialFilterData={this.props.initialFilterData} filterData={this.props.filterData}/>
</TabPanel>
</div>
)}
}
export default SimpleTabs;

Add hidden={value !== index} to the Typography in TabPanel and remove the value === index && condition inside the Typography so that the children of all the tab panels are rendered immediately but hidden (except for the currently selected one).
Example:
function TabPanel(props) {
const { children, value, index, ...other } = props;
return (
<Typography
component="div"
role="tabpanel"
hidden={value !== index}
{...other}
>
<Box p={3}>{children}</Box>
</Typography>
);
}

Related

Get Child components constants/state in parent component in React

I have a component which is a MUI TAB like this
<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}>
<Child1 />
</TabPanel>
<TabPanel value={value} index={1}>
<Child2 />
</TabPanel>
<TabPanel value={value} index={2}>
Item Three
</TabPanel>
<Box>
<Button onClick={submittab}>Submit</Button>
</Box>
</Box>
As mentioned, there are 2 child components with some textfields in it.
What I am trying to achieve is to read the child data in parent and submit it to an API as a single object.
Please help
Below the Sandbox
https://codesandbox.io/s/naughty-shadow-u2icz1?file=/demo.tsx:1233-1962
Please note I am working in typescript
Here is my child component1
import { FormLabel, TextField } from "#mui/material";
import react, { useState } from "react";
function Child1() {
const clientObj = {
user: {
name: "",
age: ""
}
};
const [client, setClient] = useState(clientObj);
const handlechange = (event: { target: { name: any; value: any } }) => {
const { name, value } = event.target;
setClient((prevState) => ({
...prevState,
context: {
...prevState.user,
[name]: value
}
}));
};
return (
<>
<FormLabel>Name</FormLabel>
<TextField
name="name"
id="text-id"
value={client.user.name}
onChange={handlechange}
/>
<FormLabel>Age</FormLabel>
<TextField
name="name"
id="text-id"
value={client.user.name}
onChange={handlechange}
/>
</>
);
}
export default Child1;
You could use a state in your parent component that will be passed as props to your children, so that they can use the parent's update state function.
Parent code:
const [state, setState] = useState()
<Child parentState={{state, setState}} />
Children code:
export function Child({parentState}) {
const {parentStateValue, setParentState} = parentState
const childTitle = 'Acme'
return <button onClick={setParentState(e => {...e, childTitle })} >childTitle</button>
}
the ideal would be to provide keys to your children so that you can manage each of their data independently in the state.

React: Using a same component twice in a page removes the data in first component

I have two tabs in my react app with each tab having a spreadsheet.
I have called the same spreadsheet tag in both the tabs. when i enter data in the first tab and move to second tab, the data in the second tab is getting replaced with the default value which i am setting in the useState.
import * as React from 'react';
import PropTypes from 'prop-types';
import Tabs from '#mui/material/Tabs';
import Tab from '#mui/material/Tab';
import Typography from '#mui/material/Typography';
import Box from '#mui/material/Box';
import Spreadsheet from './components/Spreadsheet.js'
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 sx={{ p: 3 }}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
TabPanel.propTypes = {
children: PropTypes.node,
index: PropTypes.number.isRequired,
value: PropTypes.number.isRequired,
};
function a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
export default function BasicTabs() {
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
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
<div id='div_spreadsheet1'>
<Spreadsheet id="spreadsheet1"> </Spreadsheet>
</div>
</TabPanel>
<TabPanel value={value} index={1}>
Item Two
<div id='div_spreadsheet12'>
<Spreadsheet id="spreadsheet2"> </Spreadsheet>
</div>
</TabPanel>
<TabPanel value={value} index={2}>
Item Three
</TabPanel>
</Box>
);
}
EDIT:
Adding the Spreadsheet.js code
import Spreadsheet from "react-spreadsheet";
import { useState } from "react";
export default function Sheet(){
const [data, setData] = useState([
[{ value: "" }, { value: "1" }],
]);
return(
<div>
<h4>SpreadSheet</h4>
<Spreadsheet data={data} onChange={setData} />
</div>
)
};
How to restrict this from happening. Please help.

How to make a reusable Material UI tabs component in React

I am working on a React project, and I divided some of the pages into tabs using the MUI Tab component, so I can have multiple components in one page and render each component accordingly, so I have created the Tabs component. However, I can only see one first index tab.
Reusable tab MUI component:
export default function useTabs(props) {
const { children, value, index, label, ...other } = props;
const [selectedValue, setSelectedValue] = React.useState(0);
const handleChange = (event, newValue) => {
setSelectedValue(newValue);
};
return (
<Box sx={{ width: "100%" }}>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<Tabs
value={selectedValue}
onChange={handleChange}
className={classes.tab}
textColor="primary"
indicatorColor="primary"
>
<Tab label={label} {...a11yProps(0)} className={classes.tabText} />
{/* <Tab className={classes.tabText} label={label} {...a11yProps(1)} /> */}
</Tabs>
</Box>
<TabPanel value={selectedValue} index={index}>
{children} // Rendering tab children here, but getting only the first component
</TabPanel>
</Box>
);
}
Here is how I am using it:
// Import the reusable component
import Tab from "../common/Tabs";
export default function JobsRecruitments() {
return (
<>
<Tab label="tab name" index={0}>
<MyComponent />
</Tab>
</>
)
}
I don't think it's a good idea to use children if you want reusable tabs. That's because your tabs have to be consistent: if you have 3 child nodes, you should also have 3 tab labels. If you're using children, you can easily lose track of that.
However, if you want to make a reusable tab component, I would suggest passing an array of objects containing the tab label and Component as a property to your custom Tab component. Here's an example of what I mean:
export default function BasicTabs({ tabs }) {
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<Box sx={{ width: "100%" }}>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<Tabs
value={value}
onChange={handleChange}
aria-label="basic tabs example"
>
{tabs.map(({ label }, i) => (
<Tab label={label} key={i} />
))}
</Tabs>
</Box>
{tabs.map(({ Component }, i) => (
<TabPanel value={value} index={i} key={i}>
{Component}
</TabPanel>
))}
</Box>
);
}
And then you can use this component like this:
import Tabs from "./MyTabs";
const tabs = [
{
label: "Tab 1",
Component: <div>Hello, I am tab 1</div>
},
{
label: "Tab 2",
Component: <div>Hello, I am tab 2</div>
},
{
label: "Tab 3",
Component: (
<div>
<h1>Tab with heading</h1>
<p>Hello I am a tab with a heading</p>
</div>
)
}
];
export default function App() {
return (
<div>
<Tabs tabs={tabs} />
</div>
);
}
Here's the Codesandbox with the full example

How to show contents inside a tab?

I am trying to create a form with three tabs.Now the tabs are created and i am able to switch between them but can not figure out how to display contents inside a tab
I have tried writing markup tags like h1 in between but nothing is shown on the output.
export class Stackreact extends Component {
constructor(props) {
super(props);
this.state = { value: '1' }
}
handle_change = (value) => {
this.setState({ value })
}
render() {
return (
<div>
<Tabs value={this.state.value} onChange={(e, v) => { this.handle_change(v) }} indicatorColor="primary"
textColor="primary" centered>
<Tab value='1' label='BASIC DETAILS'><h1>First Tab</h1></Tab>
<Tab value='2' label='CONTACT DETAILS'><h1>Second Tab</h1></Tab>
<Tab value='3' label='MORE DETAILS'><h1>Third tab</h1></Tab>
</Tabs>
</div>
)
}
}
I want the contents of h1 tags to be displayed in their respective tabs. Please help.Also i am new to this language.
After working a lot I have come up with this solution which is working fine.
render() {
let content_array = [<h1>First Tab</h1>, <h1>Second Tab</h1>, <h1>Third Tab</h1>];
return (
<div>
<Tabs value={this.state.value} onChange={(e, v) => { this.handle_change(v) }} indicatorColor="primary"
textColor="primary" centered>
<Tab value='1' label='BASIC DETAILS'></Tab>
<Tab value='2' label='CONTACT DETAILS'></Tab>
<Tab value='3' label='MORE DETAILS'></Tab>
</Tabs>
<Paper>
{content_array[this.state.value - 1]}
</Paper>
</div>
)
}
You can try using TabView and TabPanel from primereact
<TabView>
<TabPanel>
<h1>First Tab</h1>
<TabPanel />
<TabPanel>
<h1>Second Tab</h1>
<TabPanel />
<TabView />
You can research on your own on the props accepted by TabView and TabPanel
you can use components like this:
<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}>
Item One
</TabPanel>
<TabPanel value={value} index={1}>
Item Two
</TabPanel>
<TabPanel value={value} index={2}>
Item Three
</TabPanel>
</div>
official link : Tabs
You are trying to see the content but you've defined it in the wrong place, here you have an example of how to use Tabs and Tab, because you need to define the data that you want inside of each Tab outside the Tabs component (Tab in meant to be used as a self-closing tag).
Here is the simplest answer using hooks inspired by the accepted answer.
import { useState } from 'react';
..
const [tabState, setTabState] = useState(1);
...
const handleTabs = (value) => {
setTabState(value);
return;
}
let tabs_array = [<>{variable_with_contents_tab_1}</>, <>{tab_2_contents}</>];
Then in the render, you can provide the tab ui
<Paper className={classes.root}>
<Tabs
value={tabState}
onChange={(event, value) => { handleTabs(value) }}
indicatorColor="primary"
textColor="primary"
centered
>
<Tab value={1} label='Tab1'>
</Tab>
<Tab value={2} label="Tab2" />
</Tabs>
</Paper>
<Paper>
{tabs_array[tabState-1]}
</Paper>

Resetting view to default tab with react-web-tabs

My default tab is Income. I have a dropdown component on top of Tab. If the value in dropdown changes i need to reset the view to default tab(income). Please help me on this.
<Tabs
defaultTab="income"
onChange={(tabId) => { console.log(tabId) }}>
<TabList>
<Tab tabFor="income">Income</Tab>
<Tab tabFor="expense">Expense</Tab>
<Tab tabFor="liability">Liabilities</Tab>
</TabList>
<TabPanel tabId="income">
<p>Income</p>
</TabPanel>
<TabPanel tabId="expense">
<p>Expense</p>
</TabPanel>
<TabPanel tabId="liability">
<p>Liability</p>
</TabPanel>
</Tabs>
Make your defaultTab value as a state so it will be dynamic either and update accordingly when needed(in your case onChange value of your dropdown component.
e.g.
class TabSample extends React.Component {
state = {
defaultTab: 'Income'
}
onDropdownChange = () => {
// reset default tab value to initial state
this.setState({defaultTab: 'Income'})
}
render(){
const { defaultTab } = this.state;
return <Tabs
defaultTab={defaultTab}
onChange={(tabId) => { console.log(tabId) }}>
<TabList>
<Tab tabFor="income">Income</Tab>
<Tab tabFor="expense">Expense</Tab>
<Tab tabFor="liability">Liabilities</Tab>
</TabList>
<TabPanel tabId="income">
<p>Income</p>
</TabPanel>
<TabPanel tabId="expense">
<p>Expense</p>
</TabPanel>
<TabPanel tabId="liability">
<p>Liability</p>
</TabPanel>
</Tabs>
}
}

Resources