I just learn react and this is the problem I haven't been able to figure out for hours.
I have the App component and I want to pass data about user login to the ProtectedRoute component so I can handle access to the admin page.
In the App there is a function which manages lifting data up from the Login component (based on Firebase user authentication).
The problem is, the userLogged state is set too late so Protected route receives default value of the state.
How can I send the last state to the ProtectedRoute component?
import {useState} from "react"
import './App.css'
import Login from "./components/admin/Login"
import Homepage from "./components/homepage/Homepage"
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
import ProtectedRoute from "./ProtectedRoute"
import AdminPage from "./components/admin/AdminPage"
function App() {
const [userLogged, setUserLogged] = useState()
const getUser = (data) => {
setUserLogged(data)
console.log("data: " + data)
console.log("state: " + userLogged)
}
return (
<>
<Routes>
<Route path="/" element={<Homepage />} />
<Route path="/login" element={<Login sendUser={getUser}/>} />
<Route element={<ProtectedRoute isLoggedIn={userLogged} />}>
<Route path="/admin" element={<AdminPage />} />
</Route>
</Routes>
</>
);
}
export default App
This is the ProtectedRoute component, where I tried to add useEffect. It still doesn't work this way.
import { Navigate, Outlet } from "react-router-dom"
import {useState, useEffect} from "react"
const ProtectedRoute = (props) => {
const [isLogged, setIsLogged] = useState("")
useEffect(() => {
setIsLogged(props.isLoggedIn);
console.log(isLogged);
}, [props.isLoggedIn]);
return isLogged ? <Outlet /> : <Navigate to="/" />;
};
export default ProtectedRoute;
Related
I am trying to separate react lazy in 2 separate modules , due to the fact that I want to make my private route take multi components not only one.
my current app.tsx module looks like the following :
import React, { useState, useEffect, useContext, lazy, Suspense } from 'react';
import { BrowserRouter as Router, Route, Switch, HashRouter, Redirect } from 'react-router-dom';
// Privite Route Component
import { PrivateRoute } from './components/routes/PrivateRoute';
import { NotFound } from './components/routes/NotFound';
// Components **********************************/
import Home from './components/Home/Home';
import { AuthAction } from './components/routes/AuthAction';
// Context API
import AuthProvider from './context/auth/AuthState';
// Lazy
const AppMain = lazy(() => import('././components/App/CentralHub'));
function App() {
return (
<Router>
<AuthProvider>
<Switch>
<Route exact path='/' component={Home} />
<PrivateRoute exact path='/app' component={AppMain} />
/>
<PrivateRoute exact path='/auth/action' component={AuthAction} />
<Route path='*' component={NotFound} />
</Switch>
</AuthProvider>
</Router>
);
}
export default App;
Would calling react suspense inside of a private route that receives a that target component as propose considered a bad idea? my private rout receives this component, as props from the app.tsx now the suspense action runs from the privet route, yet my component is getting imported still from out side form (the app.tsx) should I rather split private routes and lazy import the props component inside of the private route rather than from out side ?
or would my current model considered clean regardless of the fact that lazy is working in other component than suspense dose ?
import React, { useContext, useEffect, Suspense } from 'react';
import { Redirect, Route, useHistory, useLocation } from 'react-router-dom';
import { useAppSelector } from '../../app/hooks';
interface Props {
component: React.FC<any>;
exact?: boolean;
path: string;
}
export const PrivateRoute: React.FC<Props> = ({ component: Component, ...rest }) => {
const location = useLocation();
const authenticating = useAppSelector((state) => state.auth.authenticating);
const currentUser = useAppSelector((state) => state.auth.currentUser);
return (
<>
{location.pathname.startsWith('/app') && !authenticating && (
<Route
{...rest}
render={(props) =>
!currentUser?.authenticated /*Route Condtion*/ ? (
<Redirect to='/' />
) : (
<Suspense fallback={<div></div> /*null*/}>
<Component {...props} />
</Suspense>
)
}
/>
)}
</>
);
};
I am new to reactjs and have simple question. I want to pass auth props to Login component and want to update its value from within Login component. How can I pass auth to Login component and update its value from Login component.
Secondly, I have one protected route and only logged-in users can open that component. I added Private method to implement this. Is this right approach of making some components protected ?
import "./App.css";
import {
BrowserRouter,
Routes,
Route,
Switch,
Navigate,
NavLink,
} from "react-router-dom";
import Home from "./pages/Home";
import Login from "./pages/Login";
import RequestDemo from "./pages/RequestDemo";
import ProtectedRoute from "./Protected.Route";
const auth = true; //your logic
const Private = ({ Component }) => {
return auth ? <Component /> : <Navigate to="/login" />;
};
function App() {
return (
<BrowserRouter>
<Routes>
<Route exact path="/" element={<Home />} />
<Route exact path="/Login" element={<Login />} />
<Route
path="/RequestDemo"
element={<Private Component={RequestDemo} />}
/>
</Routes>
</BrowserRouter>
);
}
export default App;
For auth variable I would suggest using as a State or context, below I have provided an example using context, you can do it with State also
import './App.css';
import { BrowserRouter,Routes, Route, Switch, Navigate, NavLink } from 'react-router-dom';
import Home from './pages/Home';
import Login from './pages/Login';
import RequestDemo from './pages/RequestDemo';
import ProtectedRoute from './Protected.Route';
import { useContext } from 'react';
// In the login Component
const LoginComponent = () => {
const authContext = useContext(MyContext);
const handleLogin = () => {
authContext.onAuthChange(true); // this will make the user login that change the value of auth to true
}
return (
<div>Login JSX</div>
)
}
const MyContext = React.createContext(null);
const Private = ({Component}) => {
const authContext = useContext(MyContext);
return authContext.auth ? <Component /> : <Navigate to="/login" />
}
function App() {
const [auth, setAuth] = React.useState(true);
const handleAuthChange = (newAuthState) => {
setAuth(newAuthState);
}
return (
<MyContext.Provider value={{
auth,
onAuthChange: handleAuthChange
}}>
<BrowserRouter>
<Routes>
<Route exact path='/' element={<Home />} />
<Route exact path='/Login' element={<Login />} />
<Route path='/RequestDemo' element={<Private Component={RequestDemo} />} />
</Routes>
</BrowserRouter>
</MyContext.Provider>
);
}
export default App;
1. In your case first you have to change auth from const to let as you want to change auth. Then you have to pass auth and an function which can update auth in the same file(where auth is declared) hence you will pass 2 props auth and function which updates auth.
From your code syntax I'm assuming you are using v6.
import "./App.css";
import {
BrowserRouter,
Routes,
Route,
Switch,
Navigate,
NavLink,
} from "react-router-dom";
import Home from "./pages/Home";
import Login from "./pages/Login";
import RequestDemo from "./pages/RequestDemo";
import ProtectedRoute from "./Protected.Route";
let auth = true; // you can mutate let and not constant
const authUpdate = (argument) => {
// update auth in this function and pass this function to the component
console.log(auth + 1);
// if you want some argument or parameter you can do so by declaring argument
console.log(argument); // only if you want to send some argument or else no need
};
const Private = ({ Component }) => {
return auth ? <Component /> : <Navigate to="/login" />;
};
function App() {
return (
<BrowserRouter>
<Routes>
<Route exact path="/" element={<Home />} />
<Route
exact
path="/Login"
element={<Login auth={auth} authUpdate={authUpdate} />} // passed auth and authUpdate as props
/>
<Route
path="/RequestDemo"
element={<Private Component={RequestDemo} />}
/>
</Routes>
</BrowserRouter>
);
}
export default App;
2. There's more than one way for protected route. Yes, you can do it like that as mentioned in code snippet.
PS: I suggest you to use state for auth if you want to trigger re-rendering and want to update auth value at parent and child component level both. If component doesn't re-render's then prop/variable won't be updated
import { useState } from "react";
const [auth,setauth]=useState(true)
const authUpdate=()=>{
// pre-steps
setauth(auth=>!auth)
// you should avoid passing setauth directly as a prop
// down the line in child component you can forget what setauth does and you can set something different
}
If you want to use certain variable/state in all child component you can look into Store or Global State concept. You can use react-redux or useContext which does not need to pass props from parent component to child component (prop drilling)
I'm new to react and is trying out the React.lazy and Suspense imports, and I just have to say, I love them!!! My website went from 45% in performance up to 50-60% and that is without optimizing images! Google search results, here I come!
However, I have a problem, I don't know how to lazy load a component which is rendered in my custom ProtectedRoute and react-router-dom v5.
The lazy loading works and takes effect when I use the React-router-doms native Route, but when I want to load a protected component via one in my custom protected routes, nothing happens, no error message in console or on the website, just a white screen. I suspect there's some problem with the import and code being put in the wrong place.
APP
import React, { Suspense } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import ProtectedRoute from "./pages/middleware/ProtectedRoute";
const Login = React.lazy(() => import("./pages/Login"));
const WebsiteCRUDs = React.lazy(() => import("./pages/WebsiteCRUDs"));
function App() {
return (
<div className="App">
<Router>
<Switch>
{/* This one works */}
<Suspense fallback={<div>Loading</div>}>
<Route exact path="/admin" component={Login} />
</Suspense>
{/* This one does NOT work */}
<Suspense fallback={<div>Loading</div>}>
<ProtectedRoute exact path="/admin/crud" component={WebsiteCRUDs} />
</Suspense>
</Switch>
</Router>
</div>
);
}
export default App;
ProtectedRoute:
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { useEffect, useState } from "react";
const ProtectedRoute = ({ component: Component, ...rest }) => {
const [isAuth, setIsAuth] = useState(false);
const [isLoading, setIsLoading] = useState(true);
// Logic validation goes here with redirect if user is not auth.
return (
<Route
{...rest}
render={(props) =>
isLoading ? (
<h1>Checking Validation</h1>
) : isAuth ? (
<Component {...props} />
) : (
<Redirect
to={{ pathname: "/admin", state: { from: props.location } }}
/>
)
}
/>
);
};
export default ProtectedRoute;
Please try like this
import React, { Suspense } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import ProtectedRoute from "./pages/middleware/ProtectedRoute";
const Login = React.lazy(() => import("./pages/Login"));
const WebsiteCRUDs = React.lazy(() => import("./pages/WebsiteCRUDs"));
function App() {
return (
<div className="App">
<Router>
<Switch>
<Suspense fallback={<div>Loading</div>}>
<Route exact path="/admin" component={Login} />
<ProtectedRoute exact path="/admin/crud" component={WebsiteCRUDs} />
</Suspense>
</Switch>
</Router>
</div>
);
}
export default App;
I'm facing a problem with react-router-dom. I'm trying to use history.push for navigating after an action conducted. but the problem is createBrowserHistory from history is updating the urls but components are not re-rendering. I've used every solution from https://stackoverflow.com/. But it's still not working as expected.
However I found a reason behind it. As my components are wrapped with connect function connect is preventing the re-render. And there was a solution too, wrap the connect function with withRouter. I tried it too. But it's not working.
Here is My App.js
import React, { Component } from "react";
import { Router, Route } from "react-router-dom";
import history from "../history"
import Navbar from "./Navbar";
import LogIn from "./LogIn";
import StreamCreate from "./streams/StreamCreate";
import StreamDelete from "./streams/StreamDelete";
import StreamEdit from "./streams/StreamEdit";
import StreamList from "./streams/StreamList";
import StreamShow from "./streams/StreamShow";
import Profile from "./streams/Profile";
class App extends Component {
render() {
return (
<div>
<Router history={history}>
<div>
<Navbar />
<Route path="/" exact component={StreamList} />
<Route path="/streams/new" exact component={StreamCreate} />
<Route path="/streams/delete" exact component={StreamDelete} />
<Route path="/streams/edit" exact component={StreamEdit} />
<Route path="/streams/show" exact component={StreamShow} />
<Route path="/login" exact component={LogIn} />
<Route path="/my-streams" exact component={Profile} />
</div>
</Router>
</div>
);
}
}
export default App;
Here is the history.js
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
Action Creator:
import Streams from "../API/Streams";
import history from "../history";
export const createStreams = (formData) => async (dispatch, getState) => {
const { userId } = getState().auth;
const response = await Streams.post("/streams", { ...formData, userId });
dispatch({ type: "CREATE_STREAM", payload: response.data });
history.push("/")
};
I got headache trying to figure out why private route is not redirecting to the path I've setting up. I think a miss something but I don't know what.
Did someone help me figure out ?
here is my private route component :
import React, {useContext} from 'react';
import {Route, Redirect} from 'react-router-dom';
import AuthContext from '../../context/auth/Authcontext';
const PrivateRoute = ({component: Component, ...rest}) => {
const authContext = useContext(AuthContext);
const {isAuthenticated, loading} = authContext;
return (
<Route
{...rest}
render={props =>
!isAuthenticated && !loading ? (
<Redirect to='/pagelist' />
) : (
<Component {...props} />
)
}
/>
);
};
export default PrivateRoute;
I'm using context to handle all the authentification part.What I'm trying to do is if you are not logging you can't access the the dashboard.
here is my App.js :
import React, {Fragment, Component} from 'react';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
import Navbar from './components/layouts/Navbar';
import Dashboard from './components/pages/Dashboard';
import Register from './components/auth/Register';
import Login from './components/auth/Login';
import PrivateRoute from './components/routing/PrivateRoute';
import AuthState from './context/auth/AuthState';
import AlertState from './context/alert/AlertState';
import setAuthToken from './utils/setAuthToken';
if (localStorage.token) {
setAuthToken(localStorage.token);
}
const App = () => {
return (
<AuthState>
<AlertState>
<Router>
<Fragment>
<Navbar />
<div className='container'>
<Alerts />
<Switch>
<PrivateRoute exact path='/' component={Dashboard} />
<Route exact path='/register' component={Register} />
<Route exact path='/login' component={Login} />
</Switch>
</div>
</Fragment>
</Router>
</AlertState>
</AuthState>
);
};
export default App;
I think I did everything correctly because when I'm watching in my console, current user are not authenticated but the dashboard is still accessible. Did I miss something ?