Adding specific items to a generic component in React - reactjs

I have main and common top Navigation Bar (TopMenu) and a body that corresponds to different modules.
/* App.js */
render() {
if (this.props.appLoading) return <Container />;
return (
<Container className="App" fluid>
<TopMenu menuStyle={menuStyle} />
<BodyContainer />
</Container>
);
}
Each module has a bunch of menu items that need to be displayed on the common Top navigation when that module loads.
/* TopMenu.js */
render() {
return (
<Menu
fixed="top"
inverted
secondary
stackable
style={this.props.menuStyle}
>
<Menu.Item as={Link} to="/" header>
<Logo />
</Menu.Item>
{ /* Module 1 Menu Items */ }
{this.props.isAuthenticated && this.props.module==='market' && (
<Menu.Menu position="left">
<Menu.Item style={{ width: '30rem' }}>
<AutoCompleteContainer />
</Menu.Item>
<Menu.Item>
<Icon name="empty star" />
</Menu.Item>
<Menu.Item>
<Icon name="line chart" />
</Menu.Item>
<Menu.Item>
<Icon name="remove bookmark" />
</Menu.Item>
<Menu.Item>
<Icon name="lock" />
</Menu.Item>
</Menu.Menu>
)}
{ /* Module 2 Menu Items */ }
{ /* Module x Menu Items */ }
<Menu.Menu position="right">
{!this.props.isAuthenticated && notAuthenticatedMenuItems}
{this.props.isAuthenticated && (
<Menu.Item>
<ProfileDropdown onSignOut={this.props.onLogOut} />
</Menu.Item>
)}
</Menu.Menu>
</Menu>
);
}
}
Clearly, having a common top navigation is making it very difficult to have all the show/hide menu items related to each module in one place. This is because there will be lot of code in one file and also, the navigation will hold the logic for showing/displaying items related to each module, which is against best practices.
My question, is how can I structure the project/code, so that I reuse the common top navigation while each module loads the required menu items when that module loads? Is there a "Extension Point" concept in React where the common top navigation bar provides an extension point for the modules to plug-in their respective items.

You could make the top generic menu as a component and then pass your respective seperate menu items as children to this Navigation component
class TopMenu extends React.Component {
render() {
return (
<Menu
fixed="top"
inverted
secondary
stackable
style={this.props.menuStyle}
>
<Menu.Item as={Link} to="/" header>
<Logo />
</Menu.Item>
{this.props.children}
<Menu.Menu position="right">
{!this.props.isAuthenticated && notAuthenticatedMenuItems}
{this.props.isAuthenticated && (
<Menu.Item>
<ProfileDropdown onSignOut={this.props.onLogOut} />
</Menu.Item>
)}
</Menu.Menu>
</Menu>
);
}
}
}
and use it as
<Container className="App" fluid>
<TopMenu menuStyle={menuStyle}>
{ /* Module 1 Menu Items */ }
{this.props.isAuthenticated && this.props.module==='market' && (
<Menu.Menu position="left">
<Menu.Item style={{ width: '30rem' }}>
<AutoCompleteContainer />
</Menu.Item>
<Menu.Item>
<Icon name="empty star" />
</Menu.Item>
<Menu.Item>
<Icon name="line chart" />
</Menu.Item>
<Menu.Item>
<Icon name="remove bookmark" />
</Menu.Item>
<Menu.Item>
<Icon name="lock" />
</Menu.Item>
</Menu
</TopMenu>
<BodyContainer />
</Container>
Similarly you could use it at other places as well

Related

In react router when routing to another page it starts from the part where i left the previous page?

I am quite new to react-router, I have this problem on a project where when I scroll down a page and then go to another page the other page start from the part where I left in the previous page but don't start from the top, so what might be the problem and its fixes?
Link of the code
Page 1
Then Routing to next page
function handleClick() {
window.scrollTo({ top: 0 });
}
export default function ElevateAppBar(props: Props) {
return (
<React.Fragment>
<CssBaseline />
<ElevationScroll {...props}>
<AppBar>
<Toolbar className="toolbar">
<Link onClick={handleClick} className="link" to="/">
Home
</Link>
<Link onClick={handleClick} className="link">
About
</Link>
<Link onClick={handleClick} to="/contact" className="link">
Contact
</Link>
</Toolbar>
</AppBar>
</ElevationScroll>
<Toolbar />
</React.Fragment>
);
}

How to show a loading spinner until component loads in React

My question is not as simple as the title. I'm trying to show a loading component for 1 second and then replace that component with the actual data.
Here is my code:
const TopNavigationAuth = () => (
<Container>
<Menu.Item
name="landing"
content="Landing"
as={Link}
to={routes.LANDING}
/>
<Menu.Item name="home" content="Home" as={Link} to={routes.HOME} />
<Menu.Menu position="right">
<Menu.Item
name="account"
content="Account"
as={Link}
to={routes.ACCOUNT}
/>
<UserSection />
<Menu.Item
name="signout"
content={
<Button
content="Sign Out"
onClick={authFunctions.doSignOut}
primary
/>
}
/>
</Menu.Menu>
</Container>
);
So here I have the <UserSection /> component which essentially just holds the user's picture and name (for now). I would like to load that component after 1 or 2 seconds but until then I would like to show a spinner instead.
I'm using semantic ui react for my app and they have a handy spinner which looks like this:
const LoaderExampleInlineCentered = () => <Loader active inline='centered' />
Can I please have some guidance with this?
You can conditionally render one of the two components, Loader Or UserSection.
this.state.profileExist === true ? <UserSection /> : <Loader />
Then initialize profileExist as a False in componentDid mount, then use setTimeout to set it to true
componentDidMount() {
this.setState({profileExist: false})
setTimeout(() => {
this.setState({profileExist: true})
}, 1000);
}

React-router4 display the name in component

I use react-router4 and try to display the name of the corresponding label in the commented location. I don't know how to solve this problem.
Does the react-router4 offer a solution to this problem? Can anyone show me how to do it?
Navigation.js:
...
<NavigationButton to="/" label="Dashboard" exact>
<MenuItem className={classes.menuItem}
selected={this.state.selected === "Dashboard"}
onClick={() => {this.setState({selected: "Dashboard"})}}>
<ListItemIcon className={classes.icon}>
<Home/>
</ListItemIcon>
<ListItemText classes={{primary: classes.primary}} inset primary="Dashboard"/>
</MenuItem>
</NavigationButton>
<NavigationButton to="/payment" label="Payment" exact>
<MenuItem className={classes.menuItem}
selected={this.state.selected === "Payment"}
onClick={() => {this.setState({selected: "Payment"})}}>
<ListItemIcon className={classes.icon}>
<Payment/>
</ListItemIcon>
<ListItemText classes={{primary: classes.primary}} inset primary="Moje płatności"/>
</MenuItem>
</NavigationButton>
...
Header:
...
<div className={classes.root}>
<div className={classes.appFrame}>
<AppBar
className={classNames(classes.appBar, {
[classes.appBarShift]: open,
[classes[`appBarShift-left`]]: open,
})}
>
<Toolbar disableGutters={!open} className={classes.toolBar}>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={this.handleDrawerToggle}
className={classNames(classes.menuButton, open)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" color="inherit" noWrap>
{/* print "Dashboard" when I choose Dashboard, or print "Payment" when I choose payment in Navigation.js. */}
</Typography>
</Toolbar>
</AppBar>
{before}
<main
className={classNames(classes.content, classes[`content-left`], {
[classes.contentShift]: open,
[classes[`contentShift-left`]]: open,
})}
>
<div className={classes.drawerHeader} />
<Switch>
<Route exact path="/" component={DashboardComponent} />
<Route exact path="/payment" component={PaymentComponent} />
<Redirect to="/" />
</Switch>
</main>
</div>
</div>
...
I use react-router4 and try to display the name of the corresponding label in the commented location. I don't know how to solve this problem.
Does the react-router4 offer a solution to this problem? Can anyone show me how to do it?
The approach is not right to display label of current component name with react-router as this will be an issue when it's a default export without even having a name. So it would be better to maintain a const LABEL = { ... } // all label with pathname as keys
So you can match this.props.location.pathname with your LABEL const object to display it properly.
const LABEL = {
dashboard : 'Dashboard',
home: 'Home'
}
in you render:
<Typography variant="h6" color="inherit" noWrap>
{LABEL[this.props.location.pathname]}
</Typography>
I hope this would help, Ask any questions if it doesn't help

admin-on-rest toggle menu (show / hide)

I'm using admin-on-rest in my react application as admin interface.
My problem is the menu, I'm add a DashboardMenuItem to toggle (show/hide) the menu. But I have no idea what the onClick function should look like and I find no example of this in the documentary or somewhere else.
Can anyone help me by giving an example for this?
My Code:
const Menu = ({ hasDashboard, onMenuTap, resources, translate, logout }) => (
<div style={styles.main}>
<WithPermission value='ROLE_SA'>
{hasDashboard && <DashboardMenuItem onClick={onMenuTap} />}
</WithPermission>
{resources
.filter(r => r.list)
.map(resource => (
<MenuItemLink
key={resource.name}
to={`/${resource.name}`}
primaryText={translatedResourceName(resource, translate)}
leftIcon={<resource.icon />}
onClick={onMenuTap}
/>
))}
{/* <MenuItemLink primaryText='Reports' key='reports' to={`/reports`} leftIcon={<UserIcon />} onClick={onMenuTap} /> */}
<WithPermission value='ROLE_SA'>
<SelectField floatingLabelText='Language for Datasets' onChange={LocaleSwitcher}>
<MenuItem value={'de'} primaryText='DE' />
<MenuItem value={'en'} primaryText='EN' />
</SelectField>
</WithPermission>
{logout}
<img src={Logo} style={{maxWidth: '100%', margin: '0 auto'}} />
</div>
)

Odd behaviour with <Menu> component

I'm using a Sider component with a Menu inside which has some Submenus on it.
When my component is static, meaning not plugged to redux, just a plain component, the Menu /> and its children work as expected.
But, in my case im trying to make my menu dynamic, so in reality my component is connected to the store using Redux and to generate all the links im using mapStateToProps and then 'connect'.
When i'm using my component with Redux, for some reason the menu doesnt work as expected. The problem boils down to ANTD not removing a class, ant-menu-inline-collapsed which basically makes my the menu not show any text even when it is expanded.
To compare current behaviours vs expected, I found the ONE difference generated on the code which is basically creating the problem
Current
Closed sider:
<div class="ant-layout-sider ant-layout-sider-collapsed" style="...">
<div class="ant-layout-sider-children">
<ul class="ant-menu ant-menu-dark ant-menu-inline-collapsed ant-menu-root ant-menu-vertical" role="menu" ...>
Opened:
<div class="ant-layout ant-layout-has-sider"><div class="ant-layout-sider" style="...">
<div class="ant-layout-sider-children">
<ul class="ant-menu ant-menu-dark **ant-menu-inline-collapsed** ant-menu-root ant-menu-vertical" role="menu">
The problem here is that the the sider is opened, the menu UL is keeping the ant-menu-inline-collapsed class. In reality it should switch it to ant-menu-inline.
I cant understand why this is happening. As soon as i remove the redux logic from my component, the Menu behaviour works fine and the class in question is removed.
The moment i put the redux logic back in, for some reason ANTD doesnt remove said class.
I'm lost as to how to proceed.
Menu configuration is the standard and it should not be affected at all as far as i know.
This is my menu code, in case you want to have a quick look:
<Menu
onClick={this.handleClick}
theme="dark"
mode="inline"
defaultSelectedKeys={['1']}
selectedKeys={[this.state.current]}>
<Menu.Item key={ROUTE_DASHBOARD}>
<Tooltip placement="right">
<Link to={ROUTE_DASHBOARD}>
<Icon type="calendar" />
<span className="nav-text">Home</span>
</Link>
</Tooltip>
</Menu.Item>
<SubMenu
key='sub2'
disabled={!budgetView}
title={<span><Icon type="mail" /><span>Top Down</span></span>}>
<Menu.Item key="3">
<Link to={`${ROUTE_BUDGET}/SS/budget/${budgetId}/version/${versionName}/${versionId}/exec`}>
<Icon type="calendar" />
<span className="nav-text">Exec Recap</span>
</Link>
</Menu.Item>
<Menu.Item key="4">
<Link to={`${ROUTE_BUDGET}/SS/budget/${budgetId}/version/${versionName}/${versionId}/total`}>
<Icon type="calendar" />
<span className="nav-text">Total</span>
</Link>
</Menu.Item>
<Menu.Item key="5">
<Link to={`${ROUTE_BUDGET}/SS/budget/${budgetId}/version/${versionName}/${versionId}/women`}>
<Icon type="calendar" />
<span className="nav-text">Women</span>
</Link>
</Menu.Item>
<Menu.Item key="6">
<Link to={`${ROUTE_BUDGET}/SS/budget/${budgetId}/version/${versionName}/${versionId}/men`}>
<Icon type="calendar" />
<span className="nav-text">Men</span>
</Link>
</Menu.Item>
</SubMenu>
<SubMenu
key="sub7"
title={<span><Icon type="team" /><span>Middle Out</span></span>}>
<Menu.Item key="3">Option 3</Menu.Item>
<Menu.Item key="4">Option 4</Menu.Item>
</SubMenu>
<SubMenu
key="sub3"
title={<span><Icon type="file" /><span>Bottom Up</span></span>}
>
<Menu.Item key="3">Buttom Up 1</Menu.Item>
<Menu.Item key="4">Buttom Up 2</Menu.Item>
<Menu.Item key="5">Buttom Up 3</Menu.Item>
</SubMenu>
<SubMenu
key="sub4"
title={<span><Icon type="area-chart" /><span>Reporting</span></span>}>
<Menu.Item key="3">Reporting-1</Menu.Item>
<Menu.Item key="4">Reporting-2</Menu.Item>
<Menu.Item key="5">Reporting-3</Menu.Item>
</SubMenu>
</Menu>

Resources