I'm trying to figure out how to use react-router for applications with react. But my route part doesn't work, it shows a blank page in any case.
Here are the code parts of the files used:
index.js
import ReactDOM from "react-dom"
import App from './components/App';
import 'bootstrap/dist/css/bootstrap.min.css'
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
App.js
import { Container } from "react-bootstrap";
import Signup from "./Signup";
import HomePage from "./HomePage";
import { AuthProvider } from "../context/AuthContext"
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
function App() {
return (
<Container className="d-flex aling-items-center justify-content-center" style={{ minHeight: "100vh" }}>
<div className="w-100" style={{ maxWidth: "400px", display: "flex", flexDirection: "column", justifyContent: "center"}}>
<Router>
<AuthProvider>
<Routes>
<Route path="/" element={<HomePage />}></Route>
<Route path="/signup" element={<Signup />}></Route>
</Routes>
</AuthProvider>
</Router>
</div>
</Container>
)
}
export default App;
Home.js and Signup.js
import React from 'react'
export default function HomePage() {
return (
<div>This is the homepage</div>
)
}
AuthContext.js and firebase.js
import React, { useContext, useState, useEffect } from 'react'
import { auth } from '../firebase'
const AuthContext = React.createContext()
export function useAuth() {
return useContext(AuthContext)
}
export function AuthProvider({children}) {
const [currentUser, setCurrentUser] = useState()
const [loading, setLoading] = useState('true')
function signup(email, password) {
return auth.createUserWithEmailAndPassword(email, password)
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user)
setLoading('false')
})
return unsubscribe
}, [])
const value = {
currentUser,
signup
}
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
)
}
//------------------(firebase.js)---------------------
import firebase from 'firebase/compat/app'
import "firebase/compat/auth"
const app = firebase.initializeApp({
apiKey: "***************************************",
authDomain: process.env.REACT_AUTH_DOMAIN,
projectId: process.env.REACT_PROJECT_ID,
storageBucket: process.env.REACT_STORAGE_BUCKET,
messagingSenderId: process.env.REACT_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_ID
})
export const auth = app.auth()
export default app
I had to specify the key as a string because writing it as the other parameters didn't work
I've also tried using different versions of react-router, reading other answered questions, or writing the code differently. Nothing worked
Don't use strings "true" and "false" in lieu of actual boolean values. Non-empty strings are always truthy. Change your loading state from strings to booleans.
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
function signup(email, password) {
return auth.createUserWithEmailAndPassword(email, password);
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, [])
const value = {
currentUser,
signup
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
}
Related
I'm trying to make a private route from my login and signup pages to my dashboard page, but all the tutorials and guides that I've stumbled upon all require some sorta AuthContext thing and I didn't implement my authentication procedure using AuthContext.
I've tried different ways but none of them work and just end up giving me a blank page when I get to the dashboard page, what can I do to make it a private route? Using Firebase v9 btw.
SignUp.js
import './Signup.css'
import React, { useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
import { auth }from '../../../firebase';
import { createUserWithEmailAndPassword, onAuthStateChanged } from 'firebase/auth';
import { Typography, Container, TextField, Button, Alert } from '#mui/material';
const Signup = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [error, setError] = useState('');
const [user, setUser] = useState({});
const history = useHistory();
onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
})
const signup = async (e) => {
e.preventDefault();
if (password !== confirmPassword) {
return setError("Passwords do not match")
}
try {
const user = await createUserWithEmailAndPassword(
auth,
email,
password
);
history.push("/dashboard/canvas");
} catch (err) {
setError(err.message);
}
}
return (
<>
<div className="text-div">
<Typography textAlign="center" variant="h3">Create a new account</Typography>
</div>
<Container className="cont" maxWidth="xl" sx={{ backgroundColor: "#ffffff", width: 500, height: "auto", borderRadius: 4, marginTop: 5, display: "flex", flexDirection: "column", padding: 5, }}>
{ error && <Alert severity="error">{error}</Alert> }
<TextField label="Email" margin="dense" type="email" onChange={ (e) => {
setEmail(e.target.value);
}}/>
<TextField label="Password" margin="dense" type="password" onChange={ (e) => {
setPassword(e.target.value);
}}/>
<TextField label="Confirm Password" margin="dense" type="password" onChange={ (e) => {
setConfirmPassword(e.target.value);
}}/>
<Button onClick={signup} variant="contained" sx={{ marginTop: 2, }}>Sign Up</Button>
<div>
Already have an account? <Link to="/login" style={{ color: '#000' }}>Log In</Link>
</div>
</Container>
</>
)
}
export default Signup;
Login.js
import './Login.css'
import React, { useState } from 'react';
import { Link, useHistory } from 'react-router-dom';
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth }from '../../../firebase';
import { Typography, Container, TextField, Button, Alert } from '#mui/material';
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const history = useHistory();
const login = async () => {
try {
const user = await signInWithEmailAndPassword(
auth,
email,
password
);
//alert("Success, user is recognized");
history.push("/dashboard/canvas");
} catch (err) {
setError("The email or password you entered is incorrect");
}
}
return (
<>
<div className="text-div">
<Typography textAlign="center" variant="h3">Login</Typography>
</div>
<Container className="cont" maxWidth="xl" sx={{ backgroundColor: "#ffffff", width: 500, height: "auto", borderRadius: 4, marginTop: 5, display: "flex", flexDirection: "column", padding: 5, }}>
{ error && <Alert severity="error">{error}</Alert> }
<TextField label="Email" margin="dense" type="email" onChange={(e) => {
setEmail(e.target.value);
}}/>
<TextField label="Password" margin="dense" type="password" onChange={(e) => {
setPassword(e.target.value);
}}/>
<Button onClick={login} variant="contained" sx={{ marginTop: 2, }}>Login</Button>
<div>
Don't have an account? <Link to="/signup" style={{ color: '#000' }}>Create one here</Link>
</div>
<div>
<Link to="/request-password-reset" style={{ color: '#000' }}>Forgot your password?</Link>
</div>
</Container>
</>
)
}
export default Login;
firebase.js
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
const firebaseConfig = {
apiKey,
authDomain,
projectId,
storageBucket,
messagingSenderId,
appId,
measurementId
}
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
export {
auth
};
App.js
import './App.css';
import Home from './components/Pages/Home';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Signup from './components/Pages/Signup/Signup';
import Login from './components/Pages/Login/Login';
import UserDashboard from './components/Pages/UserDashboard/UserDashboard';
import ForgotPassword from './components/Pages/Forgot-Password';
function App() {
return (
<Router>
<div>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/signup" component={Signup}/>
<Route path="/login" component={Login}/>
<Route path="/dashboard" component={UserDashboard}/>
<Route path="/request-password-reset" component={ForgotPassword}/>
</Switch>
</div>
</Router>
);
}
export default App;
If you are trying to create a private route component without persisting the authentication state somewhere in your app and exposed out via a React context then you will need to check the auth status asynchronously on each route change. This means you'll also need a "loading" or "pending" state while the auth status check occurring.
Here's an example implementation of just a custom private route sans any persisted state.
import { useEffect, useState } from 'react';
import { Route, Redirect } from 'react-router-dom'; // v4/5
import { onAuthStateChanged } from 'firebase/auth';
import { auth }from '../../../firebase';
const PrivateRoute = props => {
const [pending, setPending] = useState(true);
const [currentUser, setCurrentUser] = useState();
useEffect(() => {
const unsubscribe = onAuthStateChanged(
auth,
user => {
setCurrentUser(user);
setPending(false);
},
error => {
// any error logging, etc...
setPending(false);
}
);
return unsubscribe; // <-- clean up subscription
}, []);
if (pending) return null; // don't do anything yet
return currentUser
? <Route {...props} /> // <-- render route and component
: <Redirect to="/login" />; // <-- redirect to log in
};
react-router-dom#6
Custom route components are out in v6, use a layout route. The PrivateRoute component will replace Route with Outlet for nested routes to render their matched element prop into, and Navigate replaces Redirect.
import { useEffect, useState } from 'react';
import { Outlet, Navigate } from 'react-router-dom'; // v4/5
import { onAuthStateChanged } from 'firebase/auth';
import { auth }from '../../../firebase';
const PrivateRoute = props => {
const [pending, setPending] = useState(true);
const [currentUser, setCurrentUser] = useState();
useEffect(() => {
const unsubscribe = onAuthStateChanged(
auth,
user => {
setCurrentUser(user);
setPending(false);
},
error => {
// any error logging, etc...
setPending(false);
}
);
return unsubscribe; // <-- clean up subscription
}, []);
if (pending) return null; // don't do anything yet
return currentUser
? <Outlet /> // <-- render outlet for routes
: <Navigate to="/login" replace />; // <-- redirect to log in
};
Wrap the routes you want to protect.
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/signup" element={<Signup />} />
<Route path="/login" element={<Login />} />
<Route path="/request-password-reset" element={<ForgotPassword />} />
<Route element={<PrivateRoute />}>
<Route path="/dashboard" element={<UserDashboard />} />
</Route>
</Routes>
</Router>
);
}
An unauthenticated user can still see the "/account" page for a few seconds I mean there is still a delay of a few seconds before the unauthenticated user is completely redirected to another page. even though I have used ProtectedRoute, how can I make unauthenticated users unable to see the "/account" page at all without delay? i use react-router-dom 6.6.1 and firebase auth
here is for the app.js
import { ThemeProvider } from '#mui/material/styles';
import React from 'react';
import { BrowserRouter as Router, Route, Routes, Navigate} from 'react-router-dom'
import Home from './components/Home'
import Account from './components/Account';
import Signin from './components/Home/Signin';
import theme from './theme';
import { AuthContextProvider } from './components/Context/AuthContext';
import ProtectedRoute from './components/ProtectedRoute';
function App() {
return (
<ThemeProvider theme={theme}>
<AuthContextProvider>
<Router>
<Routes>
<Route path='/' element={<Home />} />
<Route path='/signin' element={<Signin />} />
<Route path='/account' element={<ProtectedRoute><Account /></ProtectedRoute>} />
</Routes>
</Router>
</AuthContextProvider>
</ThemeProvider>
)
}
export default App
and this is for the ProtectedRoute.js
import React from 'react';
import { Navigate } from 'react-router-dom';
import { UserAuth } from './Context/AuthContext';
const ProtectedRoute = ({ children }) => {
const { user } = UserAuth();
if (!user) {
return <Navigate to='/' />;
}
return children;
};
export default ProtectedRoute;
this if for the UserAuth :
import { createContext, useContext, useEffect, useState } from 'react';
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
onAuthStateChanged,
} from 'firebase/auth';
import { auth } from '../../firebase';
const UserContext = createContext();
export const AuthContextProvider = ({ children }) => {
const [user, setUser] = useState({});
const createUser = (email, password) => {
return createUserWithEmailAndPassword(auth, email, password);
};
const signIn = (email, password) => {
return signInWithEmailAndPassword(auth, email, password)
}
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
});
return () => {
unsubscribe();
};
}, []);
return (
<UserContext.Provider value={{ createUser, user, signIn }}>
{children}
</UserContext.Provider>
);
};
export const UserAuth = () => {
return useContext(UserContext);
};
I want to redirect unauthenticated users to the main page without delay
it’s probably due to the fact that your guard is validating off of !user however your initial auth state has user as {}. In this case an empty object will validate as truthy.
You could either set the initial state of user as null or you could update th guard validation to check for a value on user like if (!user.id) …
Render Protected and Base routes conditional so unauthenticated user never have access of Protected routes.
Use the following code.
Always make separate file for routing for better code readability and Separation of concerns.
BaseRoutes.js
import React from 'react'
import { Route } from 'react-router-dom'
import Home from './components/Home'
import Signin from './components/Home/Signin';
const BaseRoutes = (
<>
<Route path='/' element={<Home />} />
<Route path='/signin' element={<Signin />} />
</>
)
export default BaseRoutes
ProtectedRoutes.js
import React from 'react';
import { Route } from 'react-router-dom';
import Account from './components/Account';
const ProtectedRoutes = () => {
return (
<>
<Route path='/account' element={<Account />} />
</>
)
};
export default ProtectedRoutes;
Routes.js
import React from 'react'
import { Navigate, Route, Routes, BrowserRouter } from 'react-router-dom'
import { UserAuth } from './Context/AuthContext';
import ProtectedRoutes from './components/ProtectedRoutes';
import BaseRoutes from './components/BaseRoutes';
const Router = () => {
const { user } = UserAuth();
const isUser = Boolean(user)
const redirectUrl = isUser ? '/account' : '/signin'
return (
<BrowserRouter>
<Routes>
{isUser ? <BaseRoutes /> : <ProtectedRoutes />}
<Route
path="*"
element={<Navigate to={redirectUrl} />}
/>
</Routes>
</BrowserRouter>
)
}
export default Router
app.js
import React from 'react';
import { AuthContextProvider } from './components/Context/AuthContext';
import { ThemeProvider } from '#mui/material/styles';
import theme from './theme';
import Router from './routes'
function App() {
return (
<ThemeProvider theme={theme}>
<AuthContextProvider>
<Router />
</AuthContextProvider>
</ThemeProvider>
)
}
export default App
I tried making a global state by using a Context and have the following for my AuthProvider.js file
import { createContext, useState } from "react";
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [auth, setAuth] = useState({});
return (
<AuthContext.Provider value={{ auth, setAuth }}>
{children}
</AuthContext.Provider>
)
}
export default AuthContext;
I want to be able to change its boolean state within an if-else instead of a button with an onCLick event in the tutorial but it shows an error in the console: setAuth is not a function
function Home() {
const setAuth = useContext(AuthProvider);
const [usernameReg, setUsernameReg] = useState("");
const [passwordReg, setPasswordReg] = useState("");
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [loginStatus, setloginStatus] = useState("");
const uLogin = document.getElementById('userLogin');
const pLogin = document.getElementById('passwordLogin');
const register = () => {
if (usernameReg === "" || passwordReg === ""){
alert("Fields can't be blank");
return;
}
Axios.post('http://localhost:3001/register', {
username: usernameReg,
password: passwordReg,
}).then((response) => {
console.log(response);
});
}
const login = () => {
Axios.post('http://localhost:3001/login', {
username: username,
password: password,
}).then((response) => {
if (response.data.message){
setloginStatus(response.data.message);
alert(response.data.message);
}
else{
setloginStatus(response.data[0].username);
setAuth(true); <====================================== here
window.location.href = "/map";
}
console.log(response.data);
});
}
return (
<div className="Home">
<h1>Register</h1>
<label>Username: </label>
<input type="text"
onChange={(e) => {
setUsernameReg(e.target.value)
}}/>
<label><p></p>Password: </label>
<input type="text"
onChange={(e)=> {
setPasswordReg(e.target.value)
}}/>
<p></p>
<button onClick={register}> Register </button>
<h1>--------------------------------------------------------</h1>
{/*<form id="loginForm">*/}
<h1>Log In</h1>
<input id="userLogin" type="text" placeholder="Username"
onChange={(e) => {
setUsername(e.target.value)
}}
/>
<p></p>
<input id="passwordLogin" type="password" placeholder="Password"
onChange={(e) => {
setPassword(e.target.value)
}}
/>
<p></p>
<button onClick={login}> Log In </button>
{/*</form>*/}
<h1>{loginStatus}</h1>
</div>
)
}
my App.js is below
import React, { useState } from "react";
import { BrowserRouter,Route, Routes} from 'react-router-dom';
import './App.css';
import Home from "./Pages/Home";
import MapPage from "./Pages/MapPage.js";
import ProtectedRoutes from "./ProtectedRoutes";
import { AuthProvider } from "./components/AuthProvider"
function App() {
const [auth, setAuth ] = useState(false);
//const [auth, setAuth] = useState(false);
return (
<BrowserRouter>
<Routes element ={<AuthProvider />} value={{auth, setAuth}}>
<Route element ={<ProtectedRoutes />}>
<Route element={<MapPage />} path="/map" exact/>
</Route>
<Route path="/" element={<Home />}/>
</Routes>
</BrowserRouter>
);
}
export default App;
index.js below
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { AuthProvider } from './components/AuthProvider'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<AuthProvider>
<App />
</AuthProvider>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bitly/CRA-vitals
reportWebVitals();
I'm very new to JavaScript and I've only been following along tutorials for the functions or features I need for the project. Is there a way for this?
I'm also guessing that the code for the context file is overkill for a simple Boolean state.
You probably want to check the context docs again, and try to understand how and why you set them up, and how to access them. You tried accessing the context by passing the provider as a param to the useContext hook. This is wrong, you need to pass the Context itself. You also called the return from the context setAuth, but the return is the context itself. I urge you to read the documentation thourougly. However, this is probably what you wanted:
type AuthContextType = {
auth: boolean;
setAuth: Dispatch<SetStateAction<boolean>>;
};
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export const AuthProvider = ({ children }: PropsWithChildren<{}>) => {
const [auth, setAuth] = useState(false);
return (
<AuthContext.Provider value={{ auth, setAuth }}>
{children}
</AuthContext.Provider>
);
};
const useAuthContext = () => {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error("No AuthContext Provider found");
return ctx // as AuthContextType<...> if you need the context to be generic and to infer generics.
};
function Home() {
const { auth, setAuth } = useAuthContext();
...
}
I am trying to make it so that when a user clicks on a certain Link (Upload Component), it will redirect them to to the login component and am not sure how to accomplish this task in React. I was directed to another answered question, but it hasn't helped me as I am still confused on what I need to do with my own set up. I understand I need to make my own protected route (maybe), but I saw others accessing useContext and I do not have any file with context. I am using Version 6 in react dom. I am also using React router and redux in my project so I know I need to access the state somehow, just not sure how to wrap my mind around it so if anyone could help, I would appreciate it. The user is being stored in local storage with JWT authentication.
App.js:
import React, {useState, useEffect, useCallback} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {BrowserRouter as Router, Routes, Route} from 'react-router-dom'; //Switch was replaced by Routes in react-router-dom v6
import './App.css';
import Navbar from './components/Navbar';
import Footer from './components/Footer';
import Home from './components/pages/Home';
import Uploads from './components/pages/Uploads';
import Account from './components/pages/Account';
import Login from './components/pages/Login';
import LogOut from './components/Logout';
import Register from './components/pages/Register';
import Profile from './components/pages/Profile';
import EditProfile from './components/pages/EditProfile';
import BoardAdmin from './components/pages/BoardAdmin';
import BoardModerator from './components/pages/BoardModerator';
import BoardUser from './components/pages/BoardUser';
import {logout} from './slices/auth';
import EventBus from './common/EventBus';
const App = () => {
const [showModeratorBoard, setShowModeratorBoard] = useState(false);
const [showAdminBoard, setShowAdminBoard] = useState(false);
const {user: currentUser} = useSelector((state) => state.auth);
const dispatch = useDispatch();
useEffect(() => {
if (currentUser) {
setShowModeratorBoard(currentUser.roles.includes('ROLE_MODERATOR'));
setShowAdminBoard(currentUser.roles.includes('ROLE_ADMIN'));
} else {
setShowModeratorBoard(false);
setShowAdminBoard(false);
}
}, [currentUser]);
return (
<>
<Router>
<Navbar />
<Routes>
<Route path='/' element={<Home />} />
<Route path='/create/upload' element={<Uploads />} />
<Route path='/my-account' element={<Account />} />
<Route path='/login' element={<Login />} />
<Route path='/logout' element={<LogOut />} />
<Route path='/register' element={<Register />} />
<Route path='/profile' element={<Profile />} />
<Route path='/profile/edit' element={<EditProfile />} />
<Route path='/user' element={<BoardUser />} />
<Route path='/mod' element={<BoardModerator />} />
<Route path='/admin' element={<BoardAdmin />} />
</Routes>
<Footer />
</Router>
</>
);
};
export default App;
Auth.js:
import {createSlice, createAsyncThunk} from '#reduxjs/toolkit';
import {setMessage} from './messages';
import AuthService from '../services/auth.service';
const user = JSON.parse(localStorage.getItem('user'));
export const register = createAsyncThunk(
'auth/register',
async ({username, email, password}, thunkAPI) => {
try {
const response = await AuthService.register(username, email, password);
thunkAPI.dispatch(setMessage(response.data.message));
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const login = createAsyncThunk(
'auth/login',
async ({username, password}, thunkAPI) => {
try {
const data = await AuthService.login(username, password);
return {user: data};
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const logout = createAsyncThunk('auth/logout', async () => {
await AuthService.logout();
});
const initialState = user
? {isLoggedIn: true, user}
: {isLoggedIn: false, user: null};
const authSlice = createSlice({
name: 'auth',
initialState,
extraReducers: {
[register.fulfilled]: (state, action) => {
state.isLoggedIn = false;
},
[register.rejected]: (state, action) => {
state.isLoggedIn = false;
},
[login.fulfilled]: (state, action) => {
state.isLoggedIn = true;
state.user = action.payload.user;
},
[login.rejected]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
[logout.fulfilled]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
},
});
const {reducer} = authSlice;
export default reducer;
It looks like adding the if statement in my upload file is making it so that a logged in user can access the h1 element that is in the file, but not an unauthenticated user. So its working to an extent, just not redirecting back to login.
Upload.jsx:
import React from 'react';
import {Link} from 'react-router-dom';
import {useSelector} from 'react-redux';
function Uploads() {
const {user: currentUser} = useSelector((state) => state.auth);
if (!currentUser) {
return <Link to='/login' />;
}
return (
<>
<h1 className='page'>UPLOADS</h1>
</>
);
}
export default Uploads;
Rendering the Link doesn't imperatively navigate, use the Navigation component.
Example:
import React, { useEffect } from 'react';
import { Navigate } from 'react-router-dom';
import { useSelector } from 'react-redux';
function Uploads() {
const { user: currentUser } = useSelector((state) => state.auth);
if (!currentUser) {
return <Navigate to='/login' replace />;
}
return (
<>
<h1 className='page'>UPLOADS</h1>
</>
);
}
export default Uploads;
Typically you would protect the route instead. If you wanted to do that:
import { Navigate, Outlet } from 'react-router-dom';
import { useSelector } from 'react-redux';
const PrivateRoutes = () => {
const { user: currentUser } = useSelector((state) => state.auth);
return currentUser
? <Outlet />
: <Navigate to="/login" replace />;
}
...
<Routes>
<Route path='/' element={<Home />} />
<Route element={<PrivateRoutes />}>
<Route path='/create/upload' element={<Uploads />} />
... any other protected routes ...
</Route>
... other unprotected routes ...
</Routes>
I am trying to access LaunchDarkly flags from my App context. The code looks like:
import { useState, useEffect } from 'react';
import { Helmet } from 'react-helmet';
import { MuiPickersUtilsProvider } from '#material-ui/pickers';
import DateFnsUtils from '#date-io/date-fns';
import { ApolloProvider } from 'react-apollo';
import { Route, Router, Switch } from 'react-router-dom';
import { withLDProvider } from 'launchdarkly-react-client-sdk';
import { Loading } from '#ap/owl-ui-controls';
import { useAuth0 } from './context/Authentication';
import LabelContext from './context/LabelContext';
import { CommunityByNameContext } from './context/CommunityByNameContext';
import { segment_init } from './utils/segment';
import withConfig, { ConfigEnum } from './utils/withConfig';
import client from './utils/graphQLClient';
import labels from './config/labels';
import CurrentUser from './services/CurrentUser';
import Layout from './Layout';
import history from './utils/history';
import { SearchCommunity } from './common/Filters/SearchNameFilter/findCommunitiesQuery';
import AmplitudeContext, { AmplitudeData } from './context/AmplitudeContext';
import { DisclosuresContextProvider } from './context/DisclosuresContext';
import placeService from './services/placesService';
import { FeaturedCommunitiesIdsProvider } from './context/FeaturedCommunitiesIdsContext';
import { optimizelyInit } from './utils/optimizely';
import { FamilyFileContextProvider } from './context/FamilyFileContext';
const segmentKey = withConfig(ConfigEnum.SEGMENT_KEY);
const currentUserString = localStorage.getItem('CURRENT_USER');
const App = () => {
const { loading } = useAuth0();
const [currentUser, setCurrentUser] = useState(currentUserString ? JSON.parse(currentUserString) : null);
const [communityByName, setCommunityByName] = useState<SearchCommunity | null>(null);
const [amplitude, setAmplitude] = useState<AmplitudeData>({});
useEffect(() => {
placeService.init();
}, []);
if (loading) {
return <Loading />;
}
return (
<>
<Helmet>
<title>Beacon</title>
<script type="text/javascript">{optimizelyInit(flags)}</script>
<script type="text/javascript">{segment_init(segmentKey, currentUser)}</script>
</Helmet>
<LabelContext.Provider value={labels}>
<CommunityByNameContext.Provider value={{ communityByName, setCommunityByName }}>
<FeaturedCommunitiesIdsProvider>
<AmplitudeContext.Provider value={{ amplitude, setAmplitude }}>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<ApolloProvider client={client}>
{/* #ts-ignore */}
<CurrentUser onUserChanged={(currentUser: any) => setCurrentUser(currentUser)}>
<FamilyFileContextProvider>
<DisclosuresContextProvider>
<Router history={history}>
<Switch>
<Route path="/communities" component={Layout} />
</Switch>
</Router>
</DisclosuresContextProvider>
</FamilyFileContextProvider>
</CurrentUser>
</ApolloProvider>
</MuiPickersUtilsProvider>
</AmplitudeContext.Provider>
</FeaturedCommunitiesIdsProvider>
</CommunityByNameContext.Provider>
</LabelContext.Provider>
</>
);
};
export default withLDProvider({
clientSideID: withConfig(ConfigEnum.LD_CLIENT_SIDE_ID),
})(App);
My nested components, like for example <CurrentUser /> get LD flags, and I am able to access them as:
const CurrentUser = ({ children, flags, onUserChanged })
However, I also need LD flags inside <App /> so I could do:
<Helmet>
<title>Beacon</title>
{flags.optimizely && <script type="text/javascript">{optimizelyInit(flags)}</script>}
<script type="text/javascript">{segment_init(segmentKey, currentUser)}</script>
</Helmet>
I have tried accessing flags with const App = ({ flags }), but the value is always undefined.
I even tried sticking the below into a component
const DocumentHead = ({ flags, segmentKey, currentUser }) => (
<Helmet>
<title>Beacon</title>
<script type="text/javascript">{optimizelyInit(flags)}</script>
<script type="text/javascript">{segment_init(segmentKey, currentUser)}</script>
</Helmet>
);
Flags are still undefined.
Suggestions?
Apparently, there is a useFlags hook. We can get flags as:
const flags = useFlags();
This can be called from the <App /> component.