Home > Blockchain >  Creating a PrivateRoute context in React that works inside a Routes block
Creating a PrivateRoute context in React that works inside a Routes block

Time:12-25

I have been working on a react login service for a website (from a YouTube video) and I have been running into an issue. To preface, JavaScript is not a language I am particularly good at and I know very little about react, so please forgive any gaping holes in my knowledge of the topic.

I have attempted to make a context called PrivateRoute that serves as a Route accessible only by a logged in user. The website works when the / extension is under a Route tag, but when I change it to my custom PrivateRoute tag it says that I cannot call anything other than a Route tag inside a Routes tag. I believe the video that I am watching is using a different version of NPM and react, so that may be the cause for a lot of the issues. If that is the case what is the common practice for achieving a website, accessible only by a logged in user?

This is my code at the moment; I believe there are only two applicable files but please comment and let me know if any additional files would be helpful!

./contexts/PrivateRoute.js

import React from 'react';
import { Route, Navigate } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';

export default function PrivateRoute({ element: Element, ...rest }) {
    const { currentUser } = useAuth();
 
    return (
        <Route 
            {...rest} 
            render={props => {
                return currentUser ? <Route {...rest} element={<Element {...props} />} /> : <Navigate to="/login" replace={true} />;
            }}  
        />
    );
}

./components/App.js

import React from "react";
import Signup from './Signup';
import Dashboard from './Dashboard';
import Login from './Login';
import { Container } from 'react-bootstrap';
import { AuthProvider } from '../contexts/AuthContext';
import {BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import PrivateRoute from './PrivateRoute';

function App() {
  return (
    <Container className="d-flex align-items-center justify-content-center" style={{ minHeight: "100vh" }}>
      <div className="w-100" style={{ maxWidth: "400px" }}>
        <Router>
          <AuthProvider>
            <Routes>                                                  //Issue here, will not
              <PrivateRoute exact path="/" element={<Dashboard />} /> //allow PrivateRoute tag
              <Route path="/signup" element={<Signup />} />           //inside Routes tags
              <Route path="/login" element={<Login />} />
            </Routes>
          </AuthProvider>
        </Router>
      </div>
    </Container>    
  );
}

export default App;

I have attempted to turn the PrivateRoutes and Dashboard into one file/function but this was met with a lot of errors. I believe the issue might lie somewhere in the version of React I'm using coupled with my lack of knowledge over the changes between versions. Thanks in advance for any help! Edit: I have now remade the PrivateRoutes file again to no avail. I am still getting the error code Uncaught Error: [PrivateRoute] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment> I updated the files above accordingly.

CodePudding user response:

inside private routes wrap your route component inside routes component

 <Routes>
    <Route 
        {...rest} 
        render={props => {
            return currentUser ? <Route {...rest} element={<Element 
 {...props} />} /> : <Navigate to="/login" replace={true} />;
        }}  
    /></Routes>

inside your app component place your privateRoute component outside routes component

<Container className="d-flex align-items-center justify-content-center" style={{ minHeight: "100vh" }}>
  <div className="w-100" style={{ maxWidth: "400px" }}>
    <Router>
      <AuthProvider>
         <PrivateRoute exact path="/" element={<Dashboard />} /> 
        <Routes>                                                 
          <Route path="/signup" element={<Signup />} />       
          <Route path="/login" element={<Login />} />
        </Routes>
      </AuthProvider>
    </Router>
  </div>
</Container>    

only valid component inside your routes component is route component you cannot use other component inside it and these component must be wrapped around router component

CodePudding user response:

I ended up taking the routes entirely out of the PrivateRoutes Method

import { useAuth } from '../contexts/AuthContext';
import Dashboard from './Dashboard';
import Login from './Login';

export default function PrivateRoutes () {
    const { currentUser } = useAuth();
 
    return currentUser ? <Dashboard /> : <Login />
}

and implemented it as

<Routes>
              <Route exact path="/" element={<PrivateRoute />} />
              <Route path="/signup" element={<Signup />} />
              <Route path="/login" element={<Login />} />
</Routes>

This way took a lot of the complication out of both files, and I don't think it would be any less secure

  • Related