Write AuthContext.js in a separate file - javascript

I have written AuthContext.js in a seprate file like bellow
export const AuthContext = createContext({
isLoggedIn: false,
login: () => {},
logout: () => {},
});
const AuthContextProvider = (props) => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const loginHandler = useCallback(() => {
setIsLoggedIn(true);
}, []);
const logoutHandler = useCallback(() => {
setIsLoggedIn(false);
}, []);
const initialValue = {
isLoggedIn: isLoggedIn,
login: loginHandler,
logout: logoutHandler,
};
return (
<AuthContext.Provider value={initialValue}>
{props.children}
</AuthContext.Provider>;
);
};
export default AuthContextProvider;
Then I import them manually in App.js
I recognized that when do I login, App.js never re-render even the value of isLoggedIn changed.
Result in routes variable never gotten new values, components will not change as well.
The App.js file
const App = () => {
const authCtx = useContext(AuthContext);
const { isLoggedIn } = authCtx;
let routes;
if (isLoggedIn) {
routes = (
<Switch>
<Route path="/" exact>
<Users />
</Route>
<Route path="/places/new" exact>
<NewPlace />
</Route>
<Route path="/:userId/places" exact>
<UserPlaces />
</Route>
<Route path="/places/:placeId">
<UpdatePlace />
</Route>
<Redirect to="/" />
</Switch>
);
}
else {
routes = (
<Switch>
<Route path="/" exact>
<Users />
</Route>
<Route path="/:userId/places" exact>
<UserPlaces />
</Route>
<Route path="/signin" exact>
<SignIn />
</Route>
<Route path="/signup" exact>
<Signup />
</Route>
<Redirect to="/signin" />
</Switch>
);
}
return (
<AuthContextProvider>
<BrowserRouter>
<MainNavigation />
<main>{routes}</main>
</BrowserRouter>
</AuthContextProvider>
);
};
export default App;
What is my mistake in this case.

If App is the component rendering the AuthContextProvider component then it can't access the Context value it provides. The AuthContextProvider must be rendered higher in the ReactTree in order for App to be able to access the Context value. Current App is receiving the default value that was passed to React.createContext.
export const AuthContext = createContext({
isLoggedIn: false,
login: () => {},
logout: () => {},
});
The default value is used when there is not any Context provider higher in the ReactTree.
Move AuthContextProvider to be higher in the ReactTree, i.e. wrap App.
App
const App = () => {
const { isLoggedIn } = useContext(AuthContext);
let routes;
if (isLoggedIn) {
routes = (
<Switch>
<Route path="/" exact>
<Users />
</Route>
<Route path="/places/new" exact>
<NewPlace />
</Route>
<Route path="/:userId/places" exact>
<UserPlaces />
</Route>
<Route path="/places/:placeId">
<UpdatePlace />
</Route>
<Redirect to="/" />
</Switch>
);
}
else {
routes = (
<Switch>
<Route path="/" exact>
<Users />
</Route>
<Route path="/:userId/places" exact>
<UserPlaces />
</Route>
<Route path="/signin" exact>
<SignIn />
</Route>
<Route path="/signup" exact>
<Signup />
</Route>
<Redirect to="/signin" />
</Switch>
);
}
return (
<>
<MainNavigation />
<main>{routes}</main>
</>
);
};
export default App;
<AuthContextProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</AuthContextProvider>

Related

Maximum Depth Limit Exceeded during route protection

Can anyone tell me what's wrong in this code ??
I am protecting the route but the error is comming .
I have creted a ProtectionRoute component which calls AUTH_FUNC(Checks whether user is locked in or not) from the TwitterState.js and if the user is logged in then the ProtectionRoute returns the Component and else redirect to the login page !
App.js
import {BrowserRouter as Router , Routes , Route , useNavigate} from "react-router-dom"
import Home from "./pages/Home"
import Auth from "./pages/Auth"
import Profile from "./pages/Profile"
import Bookmark from "./pages/Bookmark"
import NotFound from "./pages/NotFound"
import Explore from "./pages/Explore"
import TrendingTags from "./pages/TrendingTags"
import Discover from "./pages/Discover"
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import Register from "./pages/Register"
import EditProfile from "./pages/EditProfile"
import ProtectedRoute from "./components/ProtectedRoute"
function App() {
return (
<Router>
<ToastContainer />
<Routes>
<Route path="/" element={<ProtectedRoute><Home/></ProtectedRoute>}/>
<Route path="/auth" element={<ProtectedRoute><Auth/></ProtectedRoute>}/>
<Route path="/profile" element={<ProtectedRoute><Profile/></ProtectedRoute>} />
<Route path="/message" element={<ProtectedRoute><NotFound/></ProtectedRoute>} />
<Route path="/notifications" element={<ProtectedRoute><NotFound/></ProtectedRoute>} />
<Route path="/bookmark" element={<ProtectedRoute><Bookmark/></ProtectedRoute>} />
<Route path="/explore" element={<ProtectedRoute><Explore/></ProtectedRoute>} />
<Route path="/explore/trending/:tagName" element={<ProtectedRoute><TrendingTags/></ProtectedRoute>} />
<Route path="/discover" element={<ProtectedRoute><Discover/></ProtectedRoute>} />
<Route path="/register" element={<ProtectedRoute><Register /></ProtectedRoute>} />
<Route path="/profile/edit" element={<ProtectedRoute><EditProfile /></ProtectedRoute>} />
</Routes>
</Router>
);
}
export default App;
ProtectedRoute.js
import React , {useContext} from "react";
import { Route, Navigate } from "react-router-dom";
import TwitterContext from "../context/TwitterContext";
export default function ProtectedRoute ({children}) {
const {AUTH_FUNC} = useContext(TwitterContext)
const loggedin = AUTH_FUNC()
if(!loggedin){
return <Navigate to="/auth"/>
}
return children
};
Twitter.jsx
import TwitterContext from "./TwitterContext";
const TwitterState = (props) => {
const AUTH_FUNC = () =>{
const res = JSON.parse(localStorage.getItem("UserData"))
if(res !== null){
return true
}
return false
}
return <TwitterContext.Provider value={{AUTH_FUNC}}>
{props.children}
</TwitterContext.Provider>
}
export default TwitterState
Error :
Issue
You are protecting the "/auth" route as well, which when a user is not authenticated yet will create a navigation loop from "/auth" to "/auth", repeat ad nauseam.
function App() {
return (
<Router>
<ToastContainer />
<Routes>
...
<Route path="/auth" element={<ProtectedRoute><Auth/></ProtectedRoute>}/>
...
</Routes>
</Router>
);
}
export default function ProtectedRoute ({ children }) {
const { AUTH_FUNC } = useContext(TwitterContext);
const loggedin = AUTH_FUNC();
if (!loggedin) {
return <Navigate to="/auth"/>;
}
return children;
};
Solution
You don't want to protect the authentication route the same way as the routes that require authentication. Remove ProtectedRoute from the "/auth" route.
Refactor the ProtectedRoute to render an Outlet also so you can make the code more DRY. This allows the ProtectedRoute component to wrap entire sets of routes that need to be protected.
import { Navigate, Outlet } from 'react-router-dom';
export default function ProtectedRoute () {
const { AUTH_FUNC } = useContext(TwitterContext);
const loggedin = AUTH_FUNC();
if (loggedin === undefined) {
return null; // or loading indicator/spinner/etc
}
return loggedin
? <Outlet />
: <Navigate to="/auth" replace />;
};
function App() {
return (
<Router>
<ToastContainer />
<Routes>
{/* Unprotected routes */}
<Route path="/auth" element={<Auth />} />
<Route path="/register" element={<Register />} />
{/* Protected routes */}
<Route element={<ProtectedRoute />}>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />} />
<Route path="/message" element={<NotFound />} />
<Route path="/notifications" element={<NotFound />} />
<Route path="/bookmark" element={<Bookmark />} />
<Route path="/explore" element={<Explore />} />
<Route path="/explore/trending/:tagName" element={<TrendingTags />} />
<Route path="/discover" element={<Discover />} />
<Route path="/profile/edit" element={<EditProfile />} />
</Route>
</Routes>
</Router>
);
}
It's also a common pattern to protect the "anonymous" routes from authenticated users. For this create another protected route component that does the inverse of the ProtectedRoute component.
import { Navigate, Outlet } from 'react-router-dom';
export default function AnonymousRoute () {
const { AUTH_FUNC } = useContext(TwitterContext);
const loggedin = AUTH_FUNC();
if (loggedin === undefined) {
return null; // or loading indicator/spinner/etc
}
return loggedin
? <Navigate to="/" replace />
: <Outlet />;
};
function App() {
return (
<Router>
<ToastContainer />
<Routes>
{/* Anonymous routes */}
<Route element={<AnonymousRoute />}>
<Route path="/auth" element={<Auth />} />
<Route path="/register" element={<Register />} />
</Route>
{/* Protected routes */}
<Route element={<ProtectedRoute />}>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />} />
<Route path="/message" element={<NotFound />} />
<Route path="/notifications" element={<NotFound />} />
<Route path="/bookmark" element={<Bookmark />} />
<Route path="/explore" element={<Explore />} />
<Route path="/explore/trending/:tagName" element={<TrendingTags />} />
<Route path="/discover" element={<Discover />} />
<Route path="/profile/edit" element={<EditProfile />} />
</Route>
</Routes>
</Router>
);
}
The Auth is in protected route and causes the navigation loop.
<Route path="/auth" element={<ProtectedRoute><Auth/></ProtectedRoute>}/>
export default function ProtectedRoute ({children}) {
const {AUTH_FUNC} = useContext(TwitterContext)
const loggedin = AUTH_FUNC()
if(!loggedin){
return <Navigate to="/auth"/>
}
return children
};
/auth route probably shouldn't be a protected route, in order to function normally.

Issue: Only renders one navbar instead of two navbars in Reactjs

const Navbar = () => {
return (
<div>
{location === '/' ? (
<AuthNav />
) : location === '/home' && isAuthenticated ? (
<MainNav />
) : <AuthNav />
}
</div>
);
};
How do I render two separate navbars on different application routes, in this case, I want to render the AuthNav in the login and signup path and I want to render MainNav on the home path.
Issues
I think you've a few things working against you:
The Navbar component is unconditionally rendered and using window.location.pathname to compute which actual navigation component to render. This means the view to be rendered is only computed when the Navbar component rerenders.
The Navbar component is rendered outside the Routes, so it's not rerendered when a route changes.
Solution
Instead of unconditionally rendering Navbar and trying to compute which nav component to render based on any current URL pathname, split them out into discrete layout routes that render the appropriate nav component.
Example:
Navbar.jsx
export const AuthNav = ({ auth }) => {
....
};
export const MainNav = () => {
....
};
App.jsx
import { Routes, Route, Navigate, Outlet } from 'react-router-dom';
import { useState } from "react";
// components
import { AuthNav, MainNav } from './components/Navbar';
// pages
...
...
const AuthLayout = ({ auth }) => (
<>
<AuthNav auth={auth} />
<Outlet />
</>
);
const MainLayout = () => (
<>
<MainNav />
<Outlet />
</>
);
const PrivateRoute = ({ auth }) => {
return auth.isAuthenticated
? <Outlet />
: <Navigate to="/" replace />;
};
const App = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
return (
<div className='parent'>
<Routes>
<Route element={<AuthLayout auth={{ isAuthenticated, setIsAuthenticated }} />}>
<Route path='/' element={<SignIn />} />
<Route path='/signup' element={<SignUp />} />
</Route>
<Route element={<MainLayout />}>
<Route element={<PrivateRoute auth={{ isAuthenticated }} />}>
<Route path='/Home' element={<Home />} />
<Route path='/music' element={<Music />} />
<Route path='/genre/' element={<Pop />} />
<Route path='/Hiphop' element={<HipHop />} />
<Route path='/Rock' element={<Rock />} />
<Route path='/EDM' element={<EDM />} />
<Route path='/Jazz' element={<Jazz />} />
<Route path='/RandB' element={<RandB />} />
<Route path='/store' element={<Store />} />
<Route path='/News' element={<News />} />
<Route path='/Contact' element={<Contact />} />
<Route path='/album/:id' element={<Album />} />
<Route path ="/album/:id/nested/" element={<Albums2 />} />
</Route>
</Route>
</Routes>
</div>
);
};

React Router v6 Private Routes - With Moralis api

I don't understand why this code is not working. "isAuthenticated" is a boolean from the moralis API. If it is true it should render outlet and if it's false should render the navigate option.
const ProtectedRoutes = () => {
const { isAuthenticated } = useMoralis();
return isAuthenticated ? <Outlet /> : <Navigate to="/" />;
};
return (
<Routes>
<Route path={ROUTER_PATHS.SIGNUP} element={<Signup />} />
<Route element={<ProtectedRoutes />}>
<Route path={ROUTER_PATHS.EMPLOYER} element={<MarketPlaceEmployer />} />
<Route path={ROUTER_PATHS.EMPLOYEE} element={<MarketPlaceEmployee />} />
</Route>
</Routes>
);
Using <Outlet /> component will make ProtectedRoutes component as Layout component to make ProtectedRoutes work as wrapping component as well you should optionally render children, change ProtectedRoutes component as,
const ProtectedRoutes = ({ children }) => {
const { isAuthenticated } = useMoralis();
if (!isAuthenticated) {
return <Navigate to="/" />;
}
return children ? children : <Outlet />;
};
Also, add exact before path,
return (
<Routes>
<Route path={ROUTER_PATHS.SIGNUP} element={<Signup />} />
<Route element={<ProtectedRoutes />}>
<Route exact path={ROUTER_PATHS.EMPLOYER} element={<MarketPlaceEmployer />} />
<Route exact path={ROUTER_PATHS.EMPLOYEE} element={<MarketPlaceEmployee />} />
</Route>
</Routes>

React-Router v6 : Lazy Loaded other library routes and components are not loading

I am new to React & React-Router#v6.
I was upgrading to react-router v6 from v5.3, and updated my local routes to v6 architecture.
v5 route file
import { lazy } from 'react';
import { Route, Switch } from 'react-router-dom';
const SchoolRoutes = () => {
const login = lazy(() => import('./login/login'));
// Component from Other Library as home page and acts as initial route
const home = lazy(() => import('#portal-school/home').then(({ Home }) => ({ default: Home })));
// Routes from Other libs
const studentRoutes = lazy(() => import('#portal-school/school').then(({ Students }) => ({ default: Students, })));
const activitiesRoutes = lazy(() => import('#portal-school/school').then(({ Activities }) => ({ default: Activities, })));
const reportRoutes = lazy(() => import('#portal-school/reports').then(({ ReportRoutes }) => ({ default: ReportRoutes, })));
return (
<Switch>
<Route exact path="/" component={home} />
<Route exact path="/login" component={login} />
<Route path="/students" component={studentRoutes} />
<Route path="/activities" component={activitiesRoutes} />
<Route path="/school-reports" component={reportRoutes} />
</Switch>
);
};
export default SchoolRoutes;
v6 route file
import React,{ lazy } from 'react';
import { Route, Routes } from 'react-router-dom';
const SchoolRoutes = () => {
const Login = lazy(() => import('./login/login'));
// Below is the home component that to be loaded.
const Home = lazy(() => import('#portal-school/home').then(({ UserLogs }) => ({ default: UserLogs })));
// Below are the routes from other libs to be loaded.
const StudentRoutes = lazy(() => import('#portal-school/school').then(({ Students }) => ({ default: Students, })));
const ActivitiesRoutes = lazy(() => import('#portal-school/school').then(({ Activities }) => ({ default: Activities, })));
const ReportRoutes = lazy(() => import('#portal-school/reports').then(({ ReportRoutes }) => ({ default: ReportRoutes, })));
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/students/*" element={<StudentRoutes />} />
<Route path="/activities/*" element={<ActivitiesRoutes />} />
<Route path="/school-reports/*" element={<ReportRoutes />} />
</Routes>
);
};
export default SchoolRoutes;
Main App component
function App(){
return (
<BrowserRouter>
<AppBar className="app-header">
<div className="school-logo"></div>
</AppBar>
<SideMenu/>
//Injecting Routes Below
<main className="main-blk">
<Suspense fallback={<CircularProgress />}> // Circular Progress is loading
// School Routes file injected below.
<SchoolRoutes/>
</Suspense>
</main>
</BrowserRouter>
)
}
So While trying to access the routes, the home screen is not loading, only Suspense fallback Message Renders, and when I try to access other Route Links, there is the same issue.
Components are not loading through the routes. Can anyone share a solution ?.
Update: I solved by moving the Suspense from App to SchoolRoutes.
Old SchoolRoutes File
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/students/*" element={<StudentRoutes />} />
<Route path="/activities/*" element={<ActivitiesRoutes />} />
<Route path="/school-reports/*" element={<ReportRoutes />} />
</Routes>
Updated SchoolRoutes File
<Suspense fallback={<CircularProgress />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/students/*" element={<StudentRoutes />} />
<Route path="/activities/*" element={<ActivitiesRoutes />} />
<Route path="/school-reports/*" element={<ReportRoutes />} />
</Routes>
<Suspense/>

Dynamic root is not rendered

I tried using Link and history. I could not access dynamic root and it will not render but the links will change.
app.js :
<>
<Switch>
<Route path='/login' exact component={Login} />
<Route path='/verification' exact component={Verification} />
<PrivateRoute path='/events' component={Events} />
<PrivateRoute path='/media' component={Media} />
<PrivateRoute path='/media/:token' component={Post} />
<Route exact path={["/store", "/store/:cats", "/store/product/:token"]}>
<StoreLayout>
<PrivateRoute path='/store' component={Store} />
<PrivateRoute path='/store/:cats' component={StoreCategory} />
<PrivateRoute path='/store/product/:token' component={Product} />
</StoreLayout>
</Route>
{showSplash ? <Splash /> : <PrivateRoute path='/' component={Home} />}
</Switch>
{alert !== null ? <GeneralAlert alert={alert} /> : null}
{authAlert !== null ? <AuthAlert alert={authAlert} /> : null}
</>
My problem is that I can not go from Media to Post:
<PrivateRoute path='/media' component={Media} />
<PrivateRoute path='/media/:token' component={Post} />
PrivateRoute.jsx :
import { useSelector } from 'react-redux';
import { Redirect, Route } from 'react-router';
const PrivateRoute = ({
path,
component: Component,
redirectTo = '/login',
exact = true,
...props
}) => {
const { authID: auth, sessionID: session } = useSelector(({ auth }) => auth);
return (
<Route
path={path}
exact={exact}
render={(props) =>
auth !== null && session !== null ? (
<Component {...props} />
) : (
<Redirect from={path} to={redirectTo} />
)
}
{...props}
/>
);
};
export default PrivateRoute;
This is one of the components in Media called MediaContent.jsx where handleClick is located.
import React from 'react';
import AuthorLogo from '../../../components/Utilities/AuthorLogo';
import AuthorName from '../../../components/Utilities/AuthorName';
import PostTime from '../../../components/Utilities/PostTime';
import { withRouter } from 'react-router-dom';
const MediaContent = ({ post, history }) => {
const handleClickPost = (e, token) => {
history.push({
pathname: '/media/' + token,
param: token,
});
};
return (
<div
onClick={(e) => handleClickPost(e, post.token)}
className='cursor-pointer'>
<h2 className={`font-bold text-md m-2 px-2`}>{post.title}</h2>
<div className={`flex justify-between items-center px-2 `}>
<div className={`flex items-center`}>
<AuthorLogo logo={post.channel.image} />
<AuthorName name={post.channel.name} />
</div>
<PostTime time={post.datetime} />
</div>
</div>
);
};
export default withRouter(MediaContent);
This is what is rendered in index.js:
import { BrowserRouter as Router } from "react-router-dom";
render(
<React.StrictMode>
<Provider store={store}>
<Router>
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
</Router>
</Provider>
</React.StrictMode>,
document.getElementById("root"),
);

Categories