App Tour
Let's take a look at the App.js file first, our entry point.
It's usually beneficial to scroll through the whole file of App.js
.
We'll start from the bottom and read the file backwards, sounds odd but it makes sense here.
At the bottom you will see a default component exported: Dashboard. This is a functional component rendering only DashboardContent. There isn't really a need to do this, so next lesson we'll refactor this out so we export a single component named App. You want the file names to generally match against the component name we export in the file, so a file named App should export a default component named App.
Scrolling up, we can examine the main component DashboardContent. This returns (renders) some React elements: A ThemeProvider wrapping a Box. Inside the Box, a CssBaseline, an AppBar a Drawer and another Box (<Box component="main"
we'll call this the "main" Box).
As you can guess, these are the top level React elements in our App. This is also an opportunity to refactor- We can abstract nested elements in the main Box and create new files for them. This will reduce the clutter of App.js and make the main components easier to read. We'll do that in our next lesson.
Aside from returning elements, the DashboardContent also manages state for the Drawer. You may think moving this to its own Drawer class might make sense, but the Appbar also needs to toggle the drawer. So, this method to toggle the drawer needs to be available to both the Drawer and the Appbar.
For now, we'll leave the state management of the drawer in our DashboardContent / soon to be renamed App component. Eventually, we will figure out a solution to manage global state.
The ThemeProvider needs to be the top level component in our App, or at least one where it is wrapping all of our Material-UI components. This is so the components can receive the theme. Here, we create an mdTheme above the DashboardContent which is calls createTheme
with no arguments. createTheme
can be customized with your own theme, which we will do a few lessons ahead. We want the app to become "our app" as early as possible, so that means setting our own theme variables to change the color, font size, spacing, and so on.
The AppBar and Drawer are using Material UI's styled
helper to style Material UI components AppBar and Drawer (these are renamed as we import them at the top of the file). We'll discuss each of these components in more detail ahead.
We then have drawerWidth, a const value that can be passed to both AppBar and Drawer, in order to determine the width of the AppBar and of course the drawer width itself.
Finally we have Copyright, a very important component to ensure your work is legally protected (Well, under US laws it is not necessary).
Okay that's enough of a tour for now, let's move onto refactoring...
import * as React from "react"; import { styled, createTheme, ThemeProvider } from "@mui/material/styles"; import CssBaseline from "@mui/material/CssBaseline"; import MuiDrawer from "@mui/material/Drawer"; import Box from "@mui/material/Box"; import MuiAppBar from "@mui/material/AppBar"; import Toolbar from "@mui/material/Toolbar"; import List from "@mui/material/List"; import Typography from "@mui/material/Typography"; import Divider from "@mui/material/Divider"; import IconButton from "@mui/material/IconButton"; import Badge from "@mui/material/Badge"; import Container from "@mui/material/Container"; import Grid from "@mui/material/Grid"; import Paper from "@mui/material/Paper"; import Link from "@mui/material/Link"; import MenuIcon from "@mui/icons-material/Menu"; import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; import NotificationsIcon from "@mui/icons-material/Notifications"; import { mainListItems, secondaryListItems } from "./listItems"; import Chart from "./Chart"; import Deposits from "./Deposits"; import Orders from "./Orders"; function Copyright(props) { return ( <Typography variant="body2" color="text.secondary" align="center" {...props} > {"Copyright © "} <Link color="inherit" href="https://mui.com/"> Your Website </Link>{" "} {new Date().getFullYear()} {"."} </Typography> ); } const drawerWidth = 240; const AppBar = styled(MuiAppBar, { shouldForwardProp: (prop) => prop !== "open", })(({ theme, open }) => ({ zIndex: theme.zIndex.drawer + 1, transition: theme.transitions.create(["width", "margin"], { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen, }), ...(open && { marginLeft: drawerWidth, width: `calc(100% - ${drawerWidth}px)`, transition: theme.transitions.create(["width", "margin"], { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.enteringScreen, }), }), })); const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== "open", })(({ theme, open }) => ({ "& .MuiDrawer-paper": { position: "relative", whiteSpace: "nowrap", width: drawerWidth, transition: theme.transitions.create("width", { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.enteringScreen, }), boxSizing: "border-box", ...(!open && { overflowX: "hidden", transition: theme.transitions.create("width", { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.leavingScreen, }), width: theme.spacing(7), [theme.breakpoints.up("sm")]: { width: theme.spacing(9), }, }), }, })); const mdTheme = createTheme(); function DashboardContent() { const [open, setOpen] = React.useState(true); const toggleDrawer = () => { setOpen(!open); }; return ( <ThemeProvider theme={mdTheme}> <Box sx={{ display: "flex" }}> <CssBaseline /> <AppBar position="absolute" open={open}> <Toolbar sx={{ pr: "24px", // keep right padding when drawer closed }} > <IconButton edge="start" color="inherit" aria-label="open drawer" onClick={toggleDrawer} sx={{ marginRight: "36px", ...(open && { display: "none" }), }} > <MenuIcon /> </IconButton> <Typography component="h1" variant="h6" color="inherit" noWrap sx={{ flexGrow: 1 }} > Dashboard </Typography> <IconButton color="inherit"> <Badge badgeContent={4} color="secondary"> <NotificationsIcon /> </Badge> </IconButton> </Toolbar> </AppBar> <Drawer variant="permanent" open={open}> <Toolbar sx={{ display: "flex", alignItems: "center", justifyContent: "flex-end", px: [1], }} > <IconButton onClick={toggleDrawer}> <ChevronLeftIcon /> </IconButton> </Toolbar> <Divider /> <List component="nav"> {mainListItems} <Divider sx={{ my: 1 }} /> {secondaryListItems} </List> </Drawer> <Box component="main" sx={{ backgroundColor: (theme) => theme.palette.mode === "light" ? theme.palette.grey[100] : theme.palette.grey[900], flexGrow: 1, height: "100vh", overflow: "auto", }} > <Toolbar /> <Container maxWidth="lg" sx={{ mt: 4, mb: 4 }}> <Grid container spacing={3}> {/* Chart */} <Grid item xs={12} md={8} lg={9}> <Paper sx={{ p: 2, display: "flex", flexDirection: "column", height: 240, }} > <Chart /> </Paper> </Grid> {/* Recent Deposits */} <Grid item xs={12} md={4} lg={3}> <Paper sx={{ p: 2, display: "flex", flexDirection: "column", height: 240, }} > <Deposits /> </Paper> </Grid> {/* Recent Orders */} <Grid item xs={12}> <Paper sx={{ p: 2, display: "flex", flexDirection: "column" }}> <Orders /> </Paper> </Grid> </Grid> <Copyright sx={{ pt: 4 }} /> </Container> </Box> </Box> </ThemeProvider> ); } export default function Dashboard() { return <DashboardContent />; }