I am making a project using ChakraUI and React. So what I wanted to do is that to add some small animations in my project. Trying to implement the ChakraUI Fade component with react router dom, but I don't know how to use it properly.
So my sidebar has multiple options. These options may or may have submenus in it. What I wanted to do is that, when someone clicks in the option or any submenu, it will load the page after the Fade transition.
What I have done so far in my sidebar component
const SidebarTitle = () => {
return (
<>
<Box
width='200px'
height='160px'
textAlign='start'
bgColor='#473198'
px={5}
borderRadius={2}
>
<Text fontSize='2xl' color='white' fontFamily='Fjord One'>
some text
</Text>
</Box>
</>
);
};
export default function Sidebar() {
const [selectedSubMenu, setSelectedSubMenu] = useState("");
const handleClick = (title) => {
if (title === selectedSubMenu) {
setSelectedSubMenu("");
} else {
setSelectedSubMenu(title);
}
};
return (
<div>
<Box
display='flex'
justifyContent='flex-start'
alignItems='flex-start'
mb={10}
>
<Box>
<SidebarTitle />
{sidebarItems.map((items) => {
return (
<Box
width='200px'
textAlign='start'
cursor='pointer'
onClick={() => {
handleClick(items.title);
}}
fontFamily='Fjord One'
boxShadow='lg'
_hover={{
bgColor: "#1a2963",
color: "white",
}}
key={items.title}
>
<Link
to={items.url}
as={RouterLink}
width='100%'
_focus={{
boxShadow: "none",
}}
style={{ textDecoration: "none" }}
>
<Text fontSize='xl'>{items.title}</Text>
</Link>
<Collapse
in={items.title === selectedSubMenu}
transition={{ enter: { delay: 0.1 }, exit: { delay: 0.1 } }}
>
{items.subMenu?.map((item) => {
return (
<Box
bgColor='#e4e8e5'
boxShadow='md'
textAlign='start'
width='200px'
color='black'
_hover={{
bgColor: "#666666",
color: "white",
}}
key={item.title}
onClick={(event) => {
event.stopPropagation();
}}
>
<Link
to={item.url}
as={RouterLink}
width='100%'
_focus={{
boxShadow: "none",
}}
style={{ textDecoration: "none" }}
>
<Text fontFamily='Fjord One'>{item.title} </Text>
</Link>
</Box>
);
})}
</Collapse>
</Box>
);
})}
</Box>
<Box width='100%'>
<Routes>
<Route path='/' element={<Home />} />
<Route
path='about'
element={<about />}
/>
<Route
path='product'
element={<product />}
/>
<Route
path='dummy-link'
element={<dummy-link />}
/>
<Route path='dummy-link' element={<dummy-link />} />
<Route path='dummy-link' element={<dummy-link />} />
<Route path='dummy-link' element={<dummy-link />} />
<Route path='dummy-link' element={<dummy-link />} />
</Routes>
</Box>
</Box>
</div>
);
}
Can someone help me on how to implement the Fade transition in react router dom?
Related
I'm a React newbie and I was following a tutorial on YouTube, which is at the time of writing this approximately 3 months old.
I got to this phase:
However then I ran into a problem. This is a piece of code that works so far:
function App() {
const [theme, colorMode] = useMode();
return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<CssBaseline />
<div className='app'>
{/* <Sidebar /> This is the const which causes the web to break for some reason */}
<main className='content'>
<Topbar />
{/* <Routes> */}
{/* <Route path='/' element={<Dashboard />} /> */}
{/* <Route path='/classification' element={<Classification />} /> */}
{/* <Route path='/timetable' element={<Timetable />} /> */}
{/* <Route path='/class' element={<Class />} /> */}
{/* <Route path='/profile' element={<Profile />} /> */}
{/* <Route path='/settings' element={<Settings />} /> */}
{/* <Route path='/notifications' element={<Notifications />} /> */}
{/* <Route path='/calendar' element={<Calendar />} /> */}
{/* </Routes> */}
</main>
</div>
</ThemeProvider>
</ColorModeContext.Provider>
);
}
export default App;
However if I uncomment the <Sidebar /> the page turns blank. As far as I tried to debug, it doesn't matter if the Sidebar file is empty or not, the page turns blank regardless.
As a React newbie, my knowledge is very limited so I haven't tried much aside of debugging. All I know is that the code in the file doesn't affect the error in any way. What I expected to happen is a sidebar to appear on the left with a couple of buttons and an image. Instead the page just turned blank. I assume this is not caused by some outdated dependencies since as I said, the code in the Sidebar file doesn't seem to affect this error. My assumption is a logical mistake I can't manage to find.
I'd love to get any sort of help with this. Furthermore, if anyone knows how to write this piece of code differently, with the same functionality (you can see in the linked video tutorial above), I will definitely be looking forward to seeing your advice. Thanks a bunch!
EDIT: This is the Sidebar file:
import { useState } from "react";
import { ProSidebar, Menu, MenuItem } from "react-pro-sidebar";
import { Box, IconButton, Typography, useTheme } from "#mui/material";
import { Link } from "react-router-dom";
import "react-pro-sidebar/dist/css/styles.css";
import { tokens } from "../../theme";
import HomeOutlinedIcon from "#mui/icons-material/HomeOutlined";
import PeopleOutlinedIcon from "#mui/icons-material/PeopleOutlined";
import ContactsOutlinedIcon from "#mui/icons-material/ContactsOutlined";
import ReceiptOutlinedIcon from "#mui/icons-material/ReceiptOutlined";
import PersonOutlinedIcon from "#mui/icons-material/PersonOutlined";
import CalendarTodayOutlinedIcon from "#mui/icons-material/CalendarTodayOutlined";
import HelpOutlineOutlinedIcon from "#mui/icons-material/HelpOutlineOutlined";
import BarChartOutlinedIcon from "#mui/icons-material/BarChartOutlined";
import PieChartOutlineOutlinedIcon from "#mui/icons-material/PieChartOutlineOutlined";
import TimelineOutlinedIcon from "#mui/icons-material/TimelineOutlined";
import MenuOutlinedIcon from "#mui/icons-material/MenuOutlined";
import MapOutlinedIcon from "#mui/icons-material/MapOutlined";
const Item = ({ title, to, icon, selected, setSelected }) => {
const theme = useTheme();
const colors = tokens(theme.palette.mode);
return (
<MenuItem
active={selected === title}
style={{
color: colors.grey[100],
}}
onClick={() => setSelected(title)}
icon={icon}
>
<Typography>{title}</Typography>
<Link to={to} />
</MenuItem>
);
};
const Sidebar = () => {
const theme = useTheme();
const colors = tokens(theme.palette.mode);
const [isCollapsed, setIsCollapsed] = useState(false);
const [selected, setSelected] = useState("Dashboard");
return (
<Box
sx={{
"& .pro-sidebar-inner": {
background: `${colors.primary[400]} !important`,
},
"& .pro-icon-wrapper": {
backgroundColor: "transparent !important",
},
"& .pro-inner-item": {
padding: "5px 35px 5px 20px !important",
},
"& .pro-inner-item:hover": {
color: "#868dfb !important",
},
"& .pro-menu-item.active": {
color: "#6870fa !important",
},
}}
>
<ProSidebar collapsed={isCollapsed}>
<Menu iconShape="square">
{/* LOGO AND MENU ICON */}
<MenuItem
onClick={() => setIsCollapsed(!isCollapsed)}
icon={isCollapsed ? <MenuOutlinedIcon /> : undefined}
style={{
margin: "10px 0 20px 0",
color: colors.grey[100],
}}
>
{!isCollapsed && (
<Box
display="flex"
justifyContent="space-between"
alignItems="center"
ml="15px"
>
<Typography variant="h3" color={colors.grey[100]}>
Project Romeo
</Typography>
<IconButton onClick={() => setIsCollapsed(!isCollapsed)}>
<MenuOutlinedIcon />
</IconButton>
</Box>
)}
</MenuItem>
{!isCollapsed && (
<Box mb="25px">
<Box display="flex" justifyContent="center" alignItems="center">
<img
alt="profile-user"
width="100px"
height="100px"
src={`../../assets/user.png`} // Database here for user profile picture or default picture?
style={{ cursor: "pointer", borderRadius: "50%" }}
/>
</Box>
<Box textAlign="center">
<Typography
variant="h2"
color={colors.grey[100]}
fontWeight="bold"
sx={{ m: "10px 0 0 0" }}
>
Vojtěch Král
</Typography>
<Typography variant="h5" color={colors.greenAccent[500]}>
Project Romeo Developer
</Typography>
</Box>
</Box>
)}
<Box paddingLeft={isCollapsed ? undefined : "10%"}>
<Item
title="Dashboard"
to="/"
icon={<HomeOutlinedIcon />}
selected={selected}
setSelected={setSelected}
/>
<Typography
variant="h6"
color={colors.grey[300]}
sx={{ m: "15px 0 5px 20px" }}
>
Data
</Typography>
<Item
title="Manage Team"
to="/team"
icon={<PeopleOutlinedIcon />}
selected={selected}
setSelected={setSelected}
/>
<Item
title="Contacts Information"
to="/contacts"
icon={<ContactsOutlinedIcon />}
selected={selected}
setSelected={setSelected}
/>
<Item
title="Invoices Balances"
to="/invoices"
icon={<ReceiptOutlinedIcon />}
selected={selected}
setSelected={setSelected}
/>
<Typography
variant="h6"
color={colors.grey[300]}
sx={{ m: "15px 0 5px 20px" }}
>
Pages
</Typography>
<Item
title="Profile Form"
to="/form"
icon={<PersonOutlinedIcon />}
selected={selected}
setSelected={setSelected}
/>
<Item
title="Calendar"
to="/calendar"
icon={<CalendarTodayOutlinedIcon />}
selected={selected}
setSelected={setSelected}
/>
<Item
title="FAQ Page"
to="/faq"
icon={<HelpOutlineOutlinedIcon />}
selected={selected}
setSelected={setSelected}
/>
<Typography
variant="h6"
color={colors.grey[300]}
sx={{ m: "15px 0 5px 20px" }}
>
Charts
</Typography>
<Item
title="Bar Chart"
to="/bar"
icon={<BarChartOutlinedIcon />}
selected={selected}
setSelected={setSelected}
/>
<Item
title="Pie Chart"
to="/pie"
icon={<PieChartOutlineOutlinedIcon />}
selected={selected}
setSelected={setSelected}
/>
<Item
title="Line Chart"
to="/line"
icon={<TimelineOutlinedIcon />}
selected={selected}
setSelected={setSelected}
/>
<Item
title="Geography Chart"
to="/geography"
icon={<MapOutlinedIcon />}
selected={selected}
setSelected={setSelected}
/>
</Box>
</Menu>
</ProSidebar>
</Box>
);
};
export default Sidebar;
i am building a ecommerce web app with react and i am unable to set state profileOptionsLayer when i click on the highlighted listitem both the logs are displayed on the console but component dosent rerender and state "profileOptionsLayer" is not updated either ,i am unable to locate the reason need help!
ALL imports .....
const TemporaryDrawer = ({
profileDrawerlayer,
setprofileDrawerlayer,
setuserDetails,
userDetails,
}) => {
const [profileOptionsLayer, setprofileOptionsLayer] = useState();
console.log(profileOptionsLayer);
return (
<>
<Drawer
anchor={"right"}
open={profileDrawerlayer}
onClose={() => {
setprofileDrawerlayer(false);
}}
>
<Box
sx={{ width: 250, background: "lightblue", height: "100%" }}
role="presentation"
onClick={() => {
setprofileDrawerlayer(false);
}}
onKeyDown={() => {
setprofileDrawerlayer(false);
}}
>
<List>
////////////////////////////////////// Below ///////////////////////////////////////////////
<ListItem disablePadding>
<ListItemButton
onClick={() => {
console.log("dsadsa");
setprofileOptionsLayer("View"); <= unable to set this state
console.log(profileOptionsLayer);
console.log("dsadsa");
}}
>
<ListItemIcon>
<VisibilityIcon />
</ListItemIcon>
<ListItemText primary={"View Profile"} />
</ListItemButton>
</ListItem>
/////////////////////////////////////// UP /////////////////////////////////////////
<ListItem
disablePadding
onClick={() => {
localStorage.removeItem("userId");
setuserDetails("");
}}
>
<ListItemButton>
<ListItemIcon>
<LogoutIcon />
</ListItemIcon>
<ListItemText primary={"Logout"} />
</ListItemButton>
</ListItem>
</List>
<Divider />
</Box>
</Drawer>
{profileOptionsLayer &&<ProfileOptions {...{ userDetails }} />}
</>
);
};
export default TemporaryDrawer;
On opening the sidebar the main content scroll does not work. Also some fields such as select options, search bar do not work when the sidebar is open. I have added the main content in the routes from where it is being loaded. The scroll, above listed functions work fine when the side-drawer is closed. I am relatively new to material ui + react and tried to workaround with some tips from other sources but I am stuck with this for a long time.
<>
<Box sx={{ display: "flex" }}>
<AppBar
sx={{ backgroundColor: "rgb(19, 71, 129)" }}
position="fixed"
className={classes.appBar}
>
<Toolbar align="center">
<IconButton
color="inherit"
onClick={handleMenuClick}
edge="start"
className={clsx(classes.menuButton)}
>
<MenuIcon />
</IconButton>
</Toolbar>
</AppBar>
<Drawer
className={classes.drawer}
variant="temporary"
open={open}
transitionDuration={{
enter: transitionDuration,
exit: transitionDuration,
}}
classes={{
paper: classes.drawerPaper,
}}
>
<Toolbar>
<IconButton
sx={{ marginLeft: "auto" }}
onClick={handleMenuClick}
edge="start"
className={clsx(classes.menuButton)}
>
<ChevronLeftIcon />
</IconButton>
</Toolbar>
<div className={classes.drawerContainer}>
{/* //Drawer Items */}
<DrawerItems open={open} isAdmin={isAdmin} />
</div>
{/* Logout */}
<LightTooltip title="Logout" placement="top">
<IconButton
sx={{ marginTop: "auto" }}
onClick={handleLogout}
color="error"
>
<ExitToAppIcon />
</IconButton>
</LightTooltip>
</Drawer>
{/* Routes for drawer components */}
<main
className={clsx(classes.content, {
[classes.contentShift]: open,
})}
>
<Routes>
<Route path="dashboard" element={<Dashboard />} />
<Route path="employees" element={<Employees />} />
<Route path="payroll" element={<Payroll />} />
<Route path="projects" element={<Projects />} />
<Route path="leaves" element={<Leaves />} />
<Route path="leavesemp" element={<LeavesEMP />} />
<Route path="empattendance" element={<EMPAttendance />} />
<Route path="hrattendance" element={<HRAttendance />} />
</Routes>
</main>
</Box>
</>
This is the style
drawerContainer: {
overflow: "auto",
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.sharp,
duration: transitionDuration,
}),
marginLeft: 0,
},
contentShift: {
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.easeOut,
duration: transitionDuration,
}),
marginLeft: drawerWidth,
},
I was in a hurry so I used a "persistent" mui drawer variant and solved the problem. I will still be looking to solve this particular problem though.
The naming conventions are off. Please ignore those.
Part of the code for the Appbar:
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1
},
title: {
flexGrow: 1
}
}));
export default function Appp() {
const classes = useStyles();
const history = useHistory();
return (
<div className={classes.root}>
<Router>
<AppBar position="static" display="flex">
<Toolbar>
<Typography variant="h6" className={classes.title}>
Employee Management
</Typography>
<Button
variant="contained"
color="secondary"
className={classes.button}
>
<Link to="/emp">Fetch Employees</Link>
</Button>
<Switch>
<Route exact path="/emp" render={(props) => <App {...props} />} />
</Switch>
</Toolbar>
</AppBar>
<br/>
</Router>
</div>
);
}
The App component ("user" is JSON data received from an API call):
return (
<div className="App">
{user &&
user.map((singleUser) => {
return (
<SimpleCard
nam={singleUser.name}
uname={singleUser.username}
ph={singleUser.phone}
wb={singleUser.website}
></SimpleCard>
);
})}
</div>
);
}
The SimpleCard Component:
export default function SimpleCard(props) {
const classes = useStyles();
return (
<Card padding={6} style={{ backgroundColor: "red", display: "inline-block" }}>
<CardContent>
<Typography
className={classes.title}
color="textSecondary"
gutterBottom
>
<h1>{props.nam}</h1>
</Typography>
<Typography variant="h5" component="h5">
#{props.uname}
</Typography>
<Typography className={classes.pos} color="textPrimary">
adjective
</Typography>
<Typography variant="body2" component="p">
well meaning and kindly.
<br />
</Typography>
</CardContent>
</Card>
);
}
So when the Fetch Employees button is clicked, I want the cards with the information to show up. However they are just getting added in the Appbar and the UI is a mess.
return (
<div className={classes.root}>
<Router>
<AppBar position="static" display="flex">
<Toolbar>
<Typography variant="h6" className={classes.title}>
Employee Management
</Typography>
<Button
variant="contained"
color="secondary"
className={classes.button}
>
<Link to="/emp">Fetch Employees</Link>
</Button>
</Toolbar>
</AppBar>
<br/>
<Switch>
<Route exact path="/emp" render={(props) => <App {...props} />} />
</Switch>
</Router>
</div>
);
You just need to remove your navigator out of the bar
i am building an online database using react and the routes have been set up such that if user is not authenticated, they will be redirected to the login page.
However, i am facing difficulties for the nested routes. i think some parts of the implementation is incorrect.
2 problems:
In order for the "404" (handled by NotFound component) page to work, exact props has to be used in <Route/>
However if exact props is used, the <Links/> found in FixedDrawer component will not work. Clicking any of them will direct to "404" page.
What am i doing wrong here?
For simplicity's sake, i will only be sharing the rendering parts of the components.
Code as below:
App.js (PageShell is only used for animating page transitions)
import LoginPage from "./pages/LoginPage";
import Dashboard from "./pages/Dashboard";
import NotFound from "./pages/NotFound";
.
.
.
render() {
const childProps = {
isAuthenticated: this.state.isAuthenticated,
userHasAuthenticated: this.userHasAuthenticated
};
return (
<div className="App">
<Switch>
<Route path="/login" exact component={PageShell(LoginPage)} />
<Route path="/" exact component={PageShell(Dashboard)} />
<Route component={NotFound} />
</Switch>
</div>
);
Dashboard.js
render() {
return (
<div>
<NavBar user={this.state.user} />
<FixedDrawer handleSetCategory={this.handleSetCategory} />
<Switch>
<Route path="/athletes" render={() => <AthletesDataTable />} />
<Route path="/races" render={() => <RacesDataTable />} />
</Switch>
</div>
);
}
FixedDrawer.js
class FixedDrawer extends Component {
constructor() {
super();
this.state = {};
}
render() {
return (
<Drawer variant="permanent" elevation={10}>
<List component="nav" style={{ top: 65 }}>
<ListItem button>
<ListItemIcon>
<FaceIcon />
</ListItemIcon>
<Link to="/athletes" style={{ textDecoration: "none" }}>
<ListItemText primary="Athletes" />
</Link>
</ListItem>
<ListItem button>
<ListItemIcon>
<GroupIcon />
</ListItemIcon>
<Link to="/teams" style={{ textDecoration: "none" }}>
<ListItemText primary="Teams" />
</Link>
</ListItem>
<ListItem button>
<ListItemIcon>
<DateRangeIcon />
</ListItemIcon>
<Link to="/meets" style={{ textDecoration: "none" }}>
<ListItemText primary="Meets" />
</Link>
</ListItem>
<ListItem button>
<ListItemIcon>
<PoolIcon />
</ListItemIcon>
<Link to="/races" style={{ textDecoration: "none" }}>
<ListItemText primary="Races" />
</Link>
</ListItem>
<ListItem button>
<ListItemIcon>
<AssignmentIcon />
</ListItemIcon>
<Link to="/results" style={{ textDecoration: "none" }}>
<ListItemText primary="Race Results" />
</Link>
</ListItem>
</List>
</Drawer>
);
}
}
"Link" component will try to match the exact route. Since, "/meets", "/teams" doesn't match the routes you provided, it will give 404 page.
return (
<div className="App">
<Navbar />
<Sidebar />
<Switch>
<Route path="/login" exact component={PageShell(LoginPage)} />
<Route path="/:val" component={PageShell(SomePage)} />
<Route path="/" exact component={PageShell(Dashboard)} />
<Route component={NotFound} />
</Switch>
<Footer />
</div>
);
I'll suggest you write the route component as above. And in the componentDidMount of "SomePage" component, you can get the value of "val" as mentioned in the path above in "this.props.match" object and render the "SomePage" component as per the routes you want.
Ex: In the didMount function of SomePage:
componentDidMount(){
this.setState({val: this.props.match.params.val})
}
render(){
return(
<div>
{
this.state.val === 'teams' ? <TeamsComponent /> : null
}
{
this.state.val === 'meets' ? <MeetsComponent /> : null
}
</div>
)
}