I have created two Drawer Navigators
const MenuDrawerRight = createDrawerNavigator();
const MenuDrawerLeft = createDrawerNavigator();
function RightDrawerScreens() {
return (
<MenuDrawerRight.Navigator
drawerContent={props => <RightDrawerContent {...props} />}
screenOptions={{
headerShown: false,
drawerType: 'back',
drawerPosition: 'right',
}}>
<MenuDrawerRight.Screen name="Menu" component={Menu} />
<MenuDrawerRight.Screen name="DashBoard" component={DashBoard} />
</MenuDrawerRight.Navigator>
);
}
function LeftDrawerScreens() {
return (
<MenuDrawerLeft.Navigator
drawerContent={props => <LeftDrawerContent {...props} />}
screenOptions={{headerShown: false}}>
<MenuDrawerLeft.Screen name="Menu" component={Menu} />
<MenuDrawerLeft.Screen name="DashBoard" component={DashBoard} />
</MenuDrawerLeft.Navigator>
);
}
created separate components for getting drawer content.
Now how to wrap these two drawers in one navigator and how to open these drawers separately(Open one drawer on first button click and open second drawer on second button click)
please ask in comment section if you need any other details.
I wrapped one drawer in another drawer like this
function MenuDrawerScreen() {
return (
<MenuDrawerRight.Navigator
drawerContent={props => <RightDrawerContent {...props} />}
screenOptions={{
headerShown: false,
drawerType: 'back',
drawerPosition: 'right',
}}
initialRouteName="MenuScreen">
<MenuDrawerRight.Screen name="MenuScreen" component={Menu} />
<MenuDrawerRight.Screen name="DashBoard" component={DashBoard} />
<MenuDrawerRight.Screen name="LeftDrawer" component={LeftDrawerScreens} />
<MenuDrawerRight.Screen name="Profile" component={Profile} />
<MenuDrawerRight.Screen name="Logout" component={Logout} />
</MenuDrawerRight.Navigator>
);
}
function LeftDrawerScreens() {
return (
<MenuDrawerLeft.Navigator
drawerContent={props => <LeftDrawerContent {...props} />}
screenOptions={{headerShown: false}}>
<MenuDrawerLeft.Screen name="Menu" component={MenuDrawerScreen} />
</MenuDrawerLeft.Navigator>
);
}
and then used navigation.getParent().openDrawer() to open parent drawer and navigation.openDrawer() to open child drawer
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 an app with React Native and Expo and i am trying to implement authentication in the app but i am getting this error and i can't find a solution.
Basically i have 2 navigation stacks AppStack & AuthStack in my App.js. AppStack will be shown if the user is authenticated and if the user is not authenticated then AuthStack will be shown to the user.
So i am conditionally rendering the stacks with ternary operator as you can see in the App.js code below. The stacks are working fine i mean if user is authenticated then AppStack is shown and if not then AuthStack is shown.
Now the Signin screen is present in AuthStack and when a user logs in successfully he should be navigated to Home screen navigation.navigate("Home") which is present in AppStack. But when a user logs in it throws me this error.
ERROR The action 'NAVIGATE' with payload {"name":"Home"} was not handled by any navigator.
Do you have a screen named 'Home'?
If you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.
This is a development-only warning and won't be shown in production
Here is the App.js code:
const AppStack = () => {
return (
<>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} options={{headerStyle: {backgroundColor: "#000"}, headerTintColor: "white"}} />
<Stack.Screen name="Forgot Password" component={ForgotPwd} options={{headerStyle: {backgroundColor: "#2a454e"}, headerTintColor: "white"}} />
<Stack.Screen name="Check Your Inbox" component={ForgotPwdNext} options={{headerStyle: {backgroundColor: "#00dfc0"}, headerTintColor: "#3a5864"}} />
<Stack.Screen name="Change Password" component={ChangePwd} options={{headerStyle: {backgroundColor: "#e5322b"}, headerTintColor: "white"}} />
</Stack.Navigator>
</>
)
}
const AuthStack = () => {
return (
<>
<Stack.Navigator>
<Stack.Screen name="Welcome" component={Welcome} options={{headerStyle: {backgroundColor: "#1b4152"}, headerTintColor: "white"}} />
<Stack.Screen name="Signin" component={Signin} options={{headerStyle: {backgroundColor: "#5549ab"}, headerTintColor: "white"}} />
<Stack.Screen name="Signup" component={Signup} options={{headerStyle: {backgroundColor: "#26dec0"}, headerTintColor: "#35474f"}} />
<Stack.Screen name="Verify Phone Number" component={VerifyPhone} options={{headerStyle: {backgroundColor: "#d268cc"}, headerTintColor: "white"}} />
<Stack.Screen name="Logged Out" component={LoggedOut} options={{headerStyle: {backgroundColor: "#5a6e7f"}, headerTintColor: "white"}} />
</Stack.Navigator>
</>
)
}
return (
<NavigationContainer style={styles.root}>
<StatusBar style="light" />
{isAuthenticated ? <AppStack /> : <AuthStack />}
</NavigationContainer>
)
}
This exact problem is addressed here.
Your AuthStack doesn't know anything about AppStack and vice versa. Hence navigation.navigate("Home") from AuthStack will fail.
When isAuthenticated is changed React Navigation will automatically navigate to correct screen. This is possible when isAuthenticated state is managed globally using React Context (or by using any state management library like Redux, Zustand etc).
Check this official example which more or less resembles your authentication flow.
You cannot navigate from Signin to Home. You can only navigate between screens that share a parent navigator or grandfather if you have nested navigators. Here is a quote from the doc talking about this specific navigator they created:
Said another way, we can only navigate to routes that have been defined on our navigator — we cannot navigate to an arbitrary component
The common way is to have a way to change isAuthenticated to true in Signin instead of navigating, and the below condition in App.js will do the job. For that you can pass a state setter from App to Signin using a context for example.
{isAuthenticated ? <AppStack /> : <AuthStack />}
somehow managed to solve the error by registering AppStack as a screen in AuthStack and also registering AuthStack as screen in AppStack like this:
const AppStack = () => {
return (
<>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} options={{headerStyle: {backgroundColor: "#000"}, headerTintColor: "white"}} />
<Stack.Screen name="Forgot Password" component={ForgotPwd} options={{headerStyle: {backgroundColor: "#2a454e"}, headerTintColor: "white"}} />
<Stack.Screen name="Check Your Inbox" component={ForgotPwdNext} options={{headerStyle: {backgroundColor: "#00dfc0"}, headerTintColor: "#3a5864"}} />
<Stack.Screen name="Change Password" component={ChangePwd} options={{headerStyle: {backgroundColor: "#e5322b"}, headerTintColor: "white"}} />
<Stack.Screen name="AuthStack" component={AuthStack} />
</Stack.Navigator>
</>
)
}
NOTE THE LAST SCREEN IN AppStack ABOVE
And same with the AppStack
const AuthStack = () => {
return (
<>
<Stack.Navigator>
<Stack.Screen name="Welcome" component={Welcome} options={{headerStyle: {backgroundColor: "#1b4152"}, headerTintColor: "white"}} />
<Stack.Screen name="Signin" component={Signin} options={{headerStyle: {backgroundColor: "#5549ab"}, headerTintColor: "white"}} />
<Stack.Screen name="Signup" component={Signup} options={{headerStyle: {backgroundColor: "#26dec0"}, headerTintColor: "#35474f"}} />
<Stack.Screen name="Verify Phone Number" component={VerifyPhone} options={{headerStyle: {backgroundColor: "#d268cc"}, headerTintColor: "white"}} />
<Stack.Screen name="Logged Out" component={LoggedOut} options={{headerStyle: {backgroundColor: "#5a6e7f"}, headerTintColor: "white"}} />
<Stack.Screen name="AppStack" component={AppStack} />
</Stack.Navigator>
</>
)
}
But now the problem is i am getting 2 navigation headers When i switch between screens which are in different stacks i mean if i go from one screen to another which are both in same stack then 2 headers doesn't appear, 2 headers only appear when i go from a screen which is in suppose Stack-A to Stack-B
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?
I wanted to set a custom header to all screens inside a stack navigator except to particular a screen, because I wanted it to have the default header. I know that setting the header property of the options props of the navigator will do the trick to apply the custom header to all the screens, but my question is how to exclude one specific screen and just set it to default?
This is my current implementation for setting the custom header to all screens:
<Stack.Navigator
screenOptions={{
header: () => <CustomHeader />,
}}
>
<Stack.Screen name="Page1" component={Page1} />
<Stack.Screen name="Page2" component={Page2} />
<Stack.Screen name="Page3" component={Page3} />
</Stack.Navigator>
What can I further add to this code to exclude the application of custom header to Page3?
Try this.
<Stack.Navigator>
<Stack.Screen name="Page1" component={Page1} />
<Stack.Screen name="Page2" component={Page2} />
<Stack.Screen options={{ header: () => <CustomHeader /> }} name="Page3" component={Page3} />
</Stack.Navigator>
If anyone was wondering how to do the same for Drawer Navigator here is the solution that I have come up with:
import React, {Fragment} from 'react';
const NavHeader = props => {
// ... NavHeader code goes here
};
export const withHeader = Component => {
return props => {
return (
<Fragment>
<NavHeader {...props} />
<Component {...props} />
</Fragment>
);
};
};
Then in your Drawer you do:
<Drawer.Navigator>
<Drawer.Screen
name={ROUTES.DASHBOARD}
component={withHeader(DashboardContainer)} // <--- Wrap it around component here.
/>
</Drawer.Navigator>
If you want all screens except one to have custom header you wrap up every component with withHeader except one.
You can set Header for all screens. For anyone which you want to avoid you can set headerShown to false.
<NavigationContainer>
<Stack.Navigator
headerMode={'screen'}
screenOptions={{
header: ({navigation}) => <Header navigation={navigation} />,
}}> // This custom Nav Header is for all screen
<Stack.Screen
name="Home"
component={HomescreenTabs}
options={{headerShown: true}}// Will user Header component as Nav Header
/>
<Stack.Screen
name="Editor"
component={Editor}
options={{headerShown: false,}} // No Nav Header component will shown
/>
<Stack.Screen
name="SearchList"
component={SearchList}
options={{headerShown: true}} // Will user Header component as Nav Header
/>
</Stack.Navigator>
</NavigationContainer>
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>
)
}