Problem with showing a component which is in another file onclick - reactjs

I am currently building an website in React with a navigation bar which I use Material-UI for.
My problem is that when I for example click "About" in my navigation bar, I want to show the content/component in About, and when I click Home I want the component Home to be shown and others hidden.
The problem is I am still a beginner in React and want to practice my React skills and now I have the navbar, Home, About in seperate files and not sure on how to pass through state, props and so in this case.
I will show a screen shot on the website and code-snippets to show my code so far.
My website:
File structure of my program:
Here is Code:
App.js:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import NavBar from './Components/Navigationbar'
import Home from './Components/Home'
import About from './Components/About'
class App extends Component {
constructor(props){
super(props);
this.state = {showAbout: true};
this.handleAbout = this.handleAbout.bind(this);
}
handleAbout(){
this.setState({showAbout: true})
}
render() {
return (
<div className="App">
<div className="App-header">
</div>
<NavBar></NavBar>
<p className="App-intro">
<Home></Home>
</p>
{this.state.showAbout ? <About /> : null}
</div>
);
}
}
export default App;
Home.jsx:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Typography from '#material-ui/core/Typography';
const useStyles = makeStyles(theme => ({
root: {
padding: theme.spacing(3, 2),
backgroundColor: 'mistyrose'
},
}));
export default function PaperSheet() {
const classes = useStyles();
return (
<div>
<Paper className={classes.root}>
<Typography variant="h5" component="h3">
Home
</Typography>
<Typography component="p">
Welcome Home
</Typography>
</Paper>
</div>
);
}
About.jsx:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Typography from '#material-ui/core/Typography';
const useStyles = makeStyles(theme => ({
root: {
padding: theme.spacing(3, 2),
backgroundColor: 'mistyrose'
},
}));
export default function PaperSheet() {
const classes = useStyles();
return (
<div>
<Paper className={classes.root}>
<Typography variant="h5" component="h3">
About
</Typography>
<Typography component="p">
About
</Typography>
</Paper>
</div>
);
}
And finally the navigation bar which is from Material UI:
Navigationbar.jsx:
import React from 'react';
import ReactDOM from 'react-dom';
import Button from '#material-ui/core/Button';
import App from '../App';
import { makeStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Tabs from '#material-ui/core/Tabs';
import Tab from '#material-ui/core/Tab';
import About from './About';
const useStyles = makeStyles({
root: {
flexGrow: 1,
},
});
function handleAbout(props){
alert('About');
}
const navBar = (props) => {
return (
<Paper >
<Tabs
//value={value}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
centered
>
<Tab label="Home" />
<Tab label="About" onClick={() => handleAbout(props)} />
<Tab label="Contact" />
</Tabs>
</Paper>
);
}
//ReactDOM.render(<navBar />, document.querySelector('#app'));
export default navBar;
My problem is I want to when I click "About" in the navbar, I want to show the About component(the content in About.jsx) on my website but not sure on how to handle state and props in the case when they are in seperate files.
Would appreciate if someone could help me.
Thanks a lot.

You can use react-router for navigation. How to install and use it is quite nicely shown on the page: https://reacttraining.com/react-router/web/guides/quick-start

Oh boy, this is a big one...
In the simplest case, you pass state though props like this:
<ChildComponent showAbout={this.state.showAbout}/>, and access it in ChildComponent by props.showAbout (or this.props.showAbout if it's a class component).
But things can get complicated as your application scales. Values can only be passed through props downwards inside the component tree; in other words, a component can only see a state that's somewhere above it. You can't use state from a sibling component or a component below it.
And that's the whole reason state management libraries exist. They provide a 'global' state that is available anywhere in the app. Redux is one of them.
You should sit down and learn Redux, as you can't really make a big app without a state management tool.
Another thing you should learn is react-router, for client-side routing.
Those things combined will provide a powerful tool for making useful apps.

Related

AppBar component of material-ui package not working in my react js application

i am creating my first React Application (WebApp) and I have following issue.
I want a Navigation Bar and therefore I am using the AppBar component of the material-ui lib. I used the the example Simple App Bar explained on the official material-ui page.
If I compile and run the app I get the following result:
Why it doesn't look like the example on the website, althought I used the same code. What am I doing wrong?
Thanks in advance.
My js file:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import Button from '#material-ui/core/Button';
import IconButton from '#material-ui/core/IconButton';
import MenuIcon from '#material-ui/icons/Menu';
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
},
}));
export default function ButtonAppBar() {
const classes = useStyles();
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton edge="start" className={classes.menuButton}
color="inherit" aria-label="menu">
<MenuIcon />
</IconButton>
<Typography variant="h6" className={classes.title}>
News
</Typography>
<Button color="inherit">Login</Button>
</Toolbar>
</AppBar>
</div>
);
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import reportWebVitals from './reportWebVitals';
import ButtonAppBar from './Components/ButtonAppBar';
ReactDOM.render(
<ButtonAppBar/>,
document.getElementById('root')
);
reportWebVitals();
UPDATE: I found the cause of my problem. I had an import of a component in my index.js file (which was not used). This component had a .css file attached which overruled the style of the AppBar.
I didn't know the .css file of a not used component has an impact, but I was wrong ^^
So as for why the color is not matching with documentation is because Material UI documentation uses it's own custom theme https://material-ui.com/customization/default-theme/, you can go to palette > Primary to get the exact color code.
As for why your nav bar looks like that I don't know I compiled the same code but got the desired result except the color, here's the code https://codesandbox.io/s/navbar-2xp1q?file=/demo.js

How do I set the onClick in the navbar of material-ui?

Below is the code for creating the navigation bar/header for an application. I have used material-ui.
import React, { Component } from "react";
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import AccountCircle from "#material-ui/icons/AccountCircle";
import ShoppingCartOutlinedIcon from "#material-ui/icons/ShoppingCartOutlined";
const styles = (theme) => ({
root: {
width: "100%",
},
flex: {
flex: 1,
},
menuButton: {
marginLeft: -12,
marginRight: 20,
},
});
class Nav extends Component {
render() {
const { classes } = this.props;
return (
<AppBar position="static" elevation={0}>
<Toolbar>
<IconButton
className={classes.menuButton}
color="contrast"
onClick={this.props.toggleDrawer}
>
<MenuIcon />
</IconButton>
<Typography className={classes.flex} type="title" color="inherit">
Pizza Shop
</Typography>
<div>
<IconButton color="contrast" onClick={this.props.cart}>
<ShoppingCartOutlinedIcon />
</IconButton>
<IconButton color="contrast" onClick={this.props.login}>
<AccountCircle />
</IconButton>
</div>
</Toolbar>
</AppBar>
);
}
}
Nav.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(Nav);
I'm new to 'props' and I'm not sure what this.props.cart or this.props.login will do. I want to create a functionality where when I click on the above two icons, I'm directed to some another component, but I'm not sure how to do it. Please help me to understand it.
Props are just parameters that the parent component sent to the children component. In your example this.props.cart and this.props.login are functions ( I am not sure about the cart, but it is used as a function ). From your example, when you click on the icons, you will call cart and login functions sent from the parent.
The answer to your question "How do you set onClick" is you already doing it on the login function/method. So you need to look at the parent component implementation.
Below I wrote a much more readable example, so feel free to look
import React from 'react'
class ChildrenComponent extends React.Component {
render() {
// This is doing the same thing, just we call the function / method in little different way
// return <div onClick={() => { this.props.onShoppingSubmit() }}>Aaa</div>
return <div onClick={this.props.onShoppingSubmit}>Aaa</div>
}
}
class ParentComponent extends React.Component {
handleShoppingSubmit = () => {
// Do what every that function needs to do
console.log('Hi from parent')
}
render() {
return (
<div>
<ChildrenComponent onShoppingSubmit={this.handleShoppingSubmit} />
</div>
)
}
}

Am I on the right track with my Material UI AppBar?

I'm working on a header for my site, and want to use Material UI. The header includes a logo, some nav links, an avatar with dropdown, etc. My question revolves around styling the nav links right now. With Material-UI, do I need to apply classes to /every/ element I want to style? For example, I currently have an app that looks like this:
import React from 'react';
import AppBar from '#material-ui/core/AppBar';
import Avatar from '#material-ui/core/Avatar';
import CssBaseline from '#material-ui/core/CssBaseline';
import IconButton from '#material-ui/core/IconButton';
import Link from '#material-ui/core/Link';
import Menu from '#material-ui/core/Menu';
import MenuItem from '#material-ui/core/MenuItem';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import theme from './theme';
import { ThemeProvider } from '#material-ui/core/styles';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles(theme => ({
root: {
backgroundColor: '#202632',
},
headerLink: {
padding: '10px 20px',
}
}));
function App() {
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = event => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const classes = useStyles();
return (
<div className="App">
<ThemeProvider theme={theme}>
<CssBaseline />
<AppBar className={classes.root}>
<Toolbar className={classes.logo}>
<img alt="Logo" src="/logo2.png" height={60} />
<Link className={classes.headerLink} href="/dashboard">
Solutions
</Link>
<Link className={classes.headerLink} href="/clientportal">
Features
</Link>
<Link className={classes.headerLink} href="/pricing">
Pricing
</Link>
<Link className={classes.headerLink}>
Our team
</Link>
<Link className={classes.headerLink}>
Blog
</Link>
<Avatar onClick={handleClick} onClose={handleClose}>
DM
</Avatar>
<Menu anchorEl={anchorEl} open={Boolean(anchorEl)}>
<MenuItem onClick={handleClose}>Item1</MenuItem>
<MenuItem onClick={handleClose}>Item2</MenuItem>
<MenuItem onClick={handleClose}>Item3</MenuItem>
</Menu>
</Toolbar>
</AppBar>
</ThemeProvider>
</div>
);
}
export default App;
The typical way I would style links is with CSS, using something like nav a { padding: 20px }, however that doesn't appear to be the idiomatic way with Material-UI. Maybe I have that assumption wrong so I wanted to verify here what the best approach for styling overrides looks like.
Thanks
You could create a SFC for the Link.
import React from ‘react’;
import Link from '#material-ui/core/Link';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles(theme => ({
headerLink: {
padding: '10px 20px',
}
}));
const AppBarLink = React.forwardRef(function AppBarLink({ children, className, ...props }, ref) {
const classes = useStyles();
return (
<Link
className={`${classes.headerLink} ${className}`}
ref={ref}
{...props}
>
{children}
</Link>
);
});
export AppBarLink;
For this particular scenario, I would use withStyles to create a styled version of Link. This will have the same effect as psyrendust's answer, but using withStyles gets rid of all the boilerplate.
import React from ‘react’;
import Link from '#material-ui/core/Link';
import { withStyles } from '#material-ui/core/styles';
const AppBarLink = withStyles({
root: {
padding: '10px 20px'
}
})(Link);
export AppBarLink;
Putting this in its own file and exporting it is only necessary if you want to leverage it from multiple files. If you are only using it in one file, then just define it there.
If you want to control props in addition to styles, you can leverage defaultProps as shown in the working example below:
import React from "react";
import Link from "#material-ui/core/Link";
import { withStyles } from "#material-ui/core/styles";
const AppBarLink = withStyles({
root: {
padding: "10px 20px"
}
})(Link);
AppBarLink.defaultProps = {
color: "secondary"
};
export default function App() {
return (
<div className="App">
<Link>Here is Link (no padding or color override)</Link>
<br />
<AppBarLink>Here is AppBarLink</AppBarLink>
<br />
<AppBarLink color="primary">
Here is AppBarLink with explicit color of primary
</AppBarLink>
</div>
);
}

How do you style a component to be under another component?

I'm setting up a small webapp using React and Material-UI, and I can't figure out how to make my drawer component fit under my app bar. Using the react developer tools I can see in the console that my components are picking up the classNames, but no matter what I do my styles aren't being applied to them. Currently, the drawer is clipping through the app bar at the top of the screen, taking up the entire height of the page instead of fitting under the app bar. What do I need to change in order to make my "Sidebar" component fit under my "Navbar" component? One of the reasons why I created these two as separate components is because I'm planning on adding more functionality to them later. Thank you.
I've been following the "clipped under the app bar" component demo at https://material-ui.com/components/drawers/ and I've also referenced Material UI Drawer won't move under Appbar
Here's a snippet of my code
My main app
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import CssBaseline from '#material-ui/core/CssBaseline';
import Navbar from './Navbar';
import Sidebar from './Sidebar';
const useStyles = makeStyles(theme => ({
App: {
display: 'flex',
},
navbar: {
zIndex: theme.zIndex.drawer + 1,
},
drawer: {
width: 240,
flexShrink: 0,
},
toolbar: theme.mixins.toolbar,
}));
function App() {
const classes = useStyles();
return (
<div className="App">
<CssBaseline/>
<Navbar position="fixed" className={classes.navbar}></Navbar>
<Sidebar className={classes.drawer}></Sidebar>
</div>
);
}
export default App;
My Navbar component
import React from 'react';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
function Navbar() {
return (
<AppBar positon="static">
<Toolbar>
<h2>Earthquake Mapper</h2>
</Toolbar>
</AppBar>
);
}
export default Navbar;
My Sidebar component
import React from 'react';
import Drawer from '#material-ui/core/Drawer';
function Sidebar() {
return (
<Drawer variant="permanent">
<div>
<ul>
<li>This</li>
<li>Will</li>
<li>Be</li>
<li>Where</li>
<li>Our</li>
<li>Data</li>
<li>Go</li>
</ul>
</div>
</Drawer>
);
}
export default Sidebar;
First, set the AppBar position as "fixed".
The way that Material-UI solves this is by adding an empty div with equal height to the Toolbar (which you put as the first child inside the AppBar component so your header min-height is equal to the Toolbar height) as the first child of the Drawer component.
This height is 64px by default, but you should get this value from theme.mixins.toolbar. You can see this in action in this tutorial.
So you'll end up doing something like this:
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Drawer from '#material-ui/core/Drawer';
const useStyles = makeStyles(theme => ({
toolbar: theme.mixins.toolbar,
}));
function Sidebar() {
const classes = useStyles();
return (
<Drawer variant="permanent">
<div className={classes.toolbar} />
<div>
<ul>
<li>This</li>
<li>Will</li>
<li>Be</li>
<li>Where</li>
<li>Our</li>
<li>Data</li>
<li>Go</li>
</ul>
</div>
</Drawer>
);
}
export default Sidebar;
Check out themes for more information on how to use theme providers and custom
theme configuration.
An alternative is to place an empty <Toolbar /> in the Drawer to fill the space.
import React from 'react';
import Drawer from '#material-ui/core/Drawer';
import Toolbar from '#material-ui/core/Toolbar';
function Sidebar() {
return (
<Drawer variant="permanent">
<Toolbar />
<div>
<ul>
<li>This</li>
<li>Will</li>
<li>Be</li>
<li>Where</li>
<li>Our</li>
<li>Data</li>
<li>Go</li>
</ul>
</div>
</Drawer>
);
}
export default Sidebar;

In react how do I get a modal to display in one component by clicking an icon in another?

I am using material UI with React. I have a modal component and a ButtonAppBar. Inside the ButtonAppBar there is a shopping cart icon that I added. I am still a bit new to React and would like to know the best way to display the modal when the shopping cart is clicked. Thanks in advance.
So here is my App.js:
import React, { Component } from 'react';
import { BrowserRouter } from 'react-router-dom';
import { Route } from 'react-router-dom';
import './App.css';
import ShopHome from './containers/ShopHome';
import ButtonAppBar from './components/ButtonAppBar';
import SimpleModalWrapped from './containers/ShoppingCartModal';
class App extends Component {
handle
render() {
return (
<BrowserRouter>
<div>
<ButtonAppBar />
<SimpleModalWrapped />
<Route exact path="/" component={ShopHome} />
</div>
</BrowserRouter>
);
}
}
export default App;
Here is my ButtonAppBar:
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import Button from '#material-ui/core/Button';
import IconButton from '#material-ui/core/IconButton';
import MenuIcon from '#material-ui/icons/Menu';
import SvgIcon from '#material-ui/core/SvgIcon';
import ShoppingCart from '#material-ui/icons/ShoppingCart';
const styles = {
root: {
flexGrow: 1,
},
grow: {
flexGrow: 1,
},
menuButton: {
marginLeft: -12,
marginRight: 20,
},
appBar: {
marginBottom: 50,
}
};
function ButtonAppBar(props) {
const { classes } = props;
return (
<div className={classes.root}>
<AppBar style={styles.appBar} position="static">
<Toolbar>
<IconButton className={classes.menuButton} color="inherit" aria-label="Menu">
<MenuIcon />
</IconButton>
<Typography variant="h6" color="inherit" className={classes.grow}>
Velo-Velo
</Typography>
<Button color="inherit">Checkout</Button>
<SvgIcon>
<ShoppingCart />
</SvgIcon>
</Toolbar>
</AppBar>
</div>
);
}
ButtonAppBar.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(ButtonAppBar);
Here is the modal:
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Typography from '#material-ui/core/Typography';
import Modal from '#material-ui/core/Modal';
import Button from '#material-ui/core/Button';
function rand() {
return Math.round(Math.random() * 20) - 10;
}
function getModalStyle() {
const top = 50 + rand();
const left = 50 + rand();
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)`,
};
}
const styles = theme => ({
paper: {
position: 'absolute',
width: theme.spacing.unit * 50,
backgroundColor: theme.palette.background.paper,
boxShadow: theme.shadows[5],
padding: theme.spacing.unit * 4,
},
});
class SimpleModal extends React.Component {
state = {
open: false,
};
handleOpen = () => {
console.log('clicked')
this.setState({ open: true });
};
handleClose = () => {
this.setState({ open: false });
};
render() {
const { classes } = this.props;
return (
<div>
{/* <Typography gutterBottom>Click to get the full Modal experience!</Typography>
<Button onClick={this.handleOpen}>Open Modal</Button> */}
<Modal
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
open={this.state.open}
onClose={this.handleClose}
>
<div style={getModalStyle()} className={classes.paper}>
<Typography variant="h6" id="modal-title">
Text in a modal
</Typography>
<Typography variant="subtitle1" id="simple-modal-description">
Duis mollis, est non commodo luctus, nisi erat porttitor ligula.
</Typography>
<SimpleModalWrapped />
</div>
</Modal>
</div>
);
}
}
SimpleModal.propTypes = {
classes: PropTypes.object.isRequired,
};
// We need an intermediary variable for handling the recursive nesting.
const SimpleModalWrapped = withStyles(styles)(SimpleModal);
export default SimpleModalWrapped;
Your best bet is to use something like Redux. As a lazier strategy have a Higher Order Component house both components. The higher order component can have the function which needs to run when you click on the icon and it changes something in the state of the higher order Component.
Say you have:
import React, { Component } from 'react'
import ComponentWithIcon from './icon-holder'
import ModalComponent from './modal'
class OuterContainer extends Component {
state = { modalOpen: false }
setModal = openOrClose => this.setState({modalOpen: openOrClose})
render(){
const {setModal, state: { modalOpen } } = this
return (
<>
<ComponentWithIcon
handleModalClick={setModal}
/>
<ModalComponent
isOpen={modalOpen}
handleModalClick={setModal}
/>
</>
)
}
}
As a result rather than the Modal component determining whether it is open (having it in its own state) which means that only it will know about whether it is presently open that will be determined by the OuterContainer... The OuterContainer is also the one which will handleTheClickEvent which clicking on the icon causes, so instead of running its own function, the icon will run the OuterComponent's function to setState...
This functionality (the clickHandler) and the state is passed to each of those child components through their props. Since the modal needs to be able to close that too can be passed in from the OuterComponent and it can be kept with the same state.modalOpen and possibly the same passed in action (clickHandler) which is toggle (or if you need for some reason then you can have an openModal/closeModal defined by the OuterComponent and pass both of those callbacks to be run by the modal.
Using redux (with react-redux) you have a single <Provider> component which is the higher order components to all of its children. The provider keeps in state a general store which is generated through redux's createStore() this store then gets actions which manipulate the central store and because it is at the top level it is able to inject its current state (or specific parts of it) to any component that is held within the Provider (which is every single component). Each of those components are able to send messages to that provider using dispatch() they then dispatch actions to the store and that results in the store being replaced with a new store that has things updated inside of it (such as whether the modal is open).
import React from 'react'
const MyModal = ({isOpen, handleModal }) => (
<div className={`modal ${isOpen ? 'is-showing' : 'is-hidden'}`}>
<div
className='toolbar'
>
<CloseButton onClick={() => handleModal(false)} />
</div>
<div>
{/* Modal content */}
</div>
</>
)

Resources