1

UPDATE

After changing PrivateRoute.js code to:

import React from 'react';
import {Navigate, Outlet} from 'react-router-dom';

const PrivateRoute = () => {
    const isAuthenticated = localStorage.getItem("authToken");

    return isAuthenticated ? <Outlet /> : <Navigate to="/login"/>
};

export default PrivateRoute;

The errors and warnings are gone but now it's just a blank screen

This is the first error

history.ts:491  Uncaught Error: A Route is only ever to be used as the child of Routes element, never rendered directly. Please wrap your Route in a Routes.
    at invariant (history.ts:491:1)
    at Route (components.tsx:353:1)
    at renderWithHooks (react-dom.development.js:16305:1)
    at mountIndeterminateComponent (react-dom.development.js:20074:1)
    at beginWork (react-dom.development.js:21587:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)
    at invokeGuardedCallback (react-dom.development.js:4277:1)
    at beginWork$1 (react-dom.development.js:27451:1)
    at performUnitOfWork (react-dom.development.js:26557:1)

This is the issue I get in my MERN Stack project in React but I am almost certain that the code is good, btw my Back-end works great

App.js

import {BrowserRouter as Router, Routes, Route} from 'react-router-dom'

//Routing
import PrivateRoute from './components/routing/PrivateRoute';

//Screens
import PrivateScreen from './components/screens/PrivateScreen'
import LoginScreen from './components/screens/LoginScreen'
import RegisterScreen from './components/screens/RegisterScreen'
import ForgotPasswordScreen from './components/screens/ForgotPasswordScreen'
import ResetPasswordScreen from './components/screens/ResetPasswordScreen'

const App = () => {
  return (
    <Router>
        <div className="app">
            <Routes>
                <Route path="/" element={<PrivateRoute />}>
                    <Route element={<PrivateScreen />} />
                </Route>
                <Route path="/login" element={<LoginScreen />} />
                <Route path="/register" element={<RegisterScreen />} />
                <Route path="/forgotpassword" element={<ForgotPasswordScreen />} />
                <Route path="/passwordreset/:resetToken" element={< ResetPasswordScreen />} />
            </Routes>
        </div>
    </Router>
  );
}

export default App;

PrivateRoute.js

import React from 'react';
import { Route, Navigate } from 'react-router-dom';

const PrivateRoute = ({ component: Component, ...rest }) => {
    const isAuthenticated = localStorage.getItem("authToken");

    if (isAuthenticated) {
        return <Route {...rest} element={<Component />} />;
    } else {
        return <Navigate to="/login" />;
    }
};

export default PrivateRoute;

Btw there is an warning in this script for line 8 that says

react-jsx-dev-runtime.development.js:87  Warning: React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check your code at PrivateRoute.js:8.
    at PrivateRoute (http://localhost:3000/static/js/bundle.js:189:14)
    at RenderedRoute (http://localhost:3000/static/js/bundle.js:40635:5)
    at Routes (http://localhost:3000/static/js/bundle.js:41267:5)
    at div
    at Router (http://localhost:3000/static/js/bundle.js:41205:15)
    at BrowserRouter (http://localhost:3000/static/js/bundle.js:39306:5)
    at App

And I can't seem to figure out why

PrivateScreen.js

import {useState, useEffect} from "react";
import axios from 'axios';
import {useNavigate} from "react-router-dom";


const PrivateScreen = () =>{

    const history = useNavigate();

    const [error, setError] = useState("");
    const [privateData, setPrivateData] = useState("");

    useEffect(() => {
        if(!localStorage.getItem("authToken")){
            history("/login");
        }

        const fetchPrivateData = async () => {

            const config = {
                headers: {
                    "Content-Type": "application/json",
                    Authorization: `Bearer ${localStorage.getItem("authToken")}`
                }
            }

            try {
                const {data} = await axios.get("/api/private", config);
                setPrivateData(data.data);
            }catch (e) {
                localStorage.removeItem("authToken");
                setError("You are not authorized, please login");
            }
        }

        fetchPrivateData().then().catch(error => {
            console.log("Error:", error);
        });
    }, [history]);

    const logoutHandler = () => {
        localStorage.removeItem("authToken");
        history("/login");
    }

    return error ? (
         <span className="error-message">{error}</span>
    ) : (
        <>
            <div style={{background: "green", color:"white"}}>{privateData}</div>
            <button onClick={logoutHandler} className="">Logout</button>
        </>
    );
};

export  default PrivateScreen;

I went on many websites like StackOverflow and GitHub, I read the documentation for react-router-dom v6, I switched from history to useNavigate, I tried with AI assistents and a friend checked my code but nothing seemed to work

The code works only first time but when it redirects to PrivateScreen it just starts showing these errors

VLAZ
  • 26,331
  • 9
  • 49
  • 67
Strahinja
  • 19
  • 5
  • 1
    You've got a `` inside a `` – Pointy Aug 30 '23 at 15:36
  • @Pointy , It was the other way but I faced the same issue so I changed it to this from this }/>}> – Strahinja Aug 30 '23 at 15:41
  • 1
    `PrivateRoute` is rendering a `Route` directly, which is an invariant violation (*e.g. the error you see*). Wrapping `Route` in `Routes` would fix the error but your code still won't work since `PrivateRoute` needs to render an `Outlet` instead so the nested routes can render their content. – Drew Reese Aug 30 '23 at 15:47
  • @DrewReese so basically, I need to surround Route from PrivateRoute script in Routes? But I didn't quite understand the 2nd part about Outlet, I am new to react, how should I do that? – Strahinja Aug 30 '23 at 15:51
  • No, your `App` code and routes are fine. You are quasi-mixing RRD v5 and v6 route protection patterns. `PrivateRoute` should take no props and render `` instead of a `Route`. It's the `Route` in `PrivateRoute` that is the issue. Just study the RRDv6 examples in the linked duplicate. – Drew Reese Aug 30 '23 at 15:53
  • @DrewReese okay now all the errors and warnings are gone but now I just get blank screen and it doesn't redirect me to login here is the PrivateRoute import React from 'react'; import {Route, Navigate, Routes, Outlet} from 'react-router-dom'; const PrivateRoute = () => { const isAuthenticated = localStorage.getItem("authToken"); if (isAuthenticated) { return ( } /> ); } else { return ; } }; export default PrivateRoute; – Strahinja Aug 30 '23 at 16:01
  • No no, just `return isAuthenticated ? : `. Using localStorage might also not be the best idea since updating it doesn't trigger any component rerenders. Better to use state in the parent and pass down as props, or use a React Context or a state management library like Redux. LocalStorage can be used to persist app state, but it shouldn't generally be used live. – Drew Reese Aug 30 '23 at 16:03
  • @DrewReese ok I did and now it's the same blank screen, I posted update on the top of the page }/>}/> I'm wondering is this okay in App? – Strahinja Aug 30 '23 at 16:10
  • Just follow the example code in the marked duplicate. If you are still having trouble though see if you can create a ***running*** [codesandbox](https://codesandbox.io/) demo of your code that reproduces the issue you are seeing that we can inspect live. – Drew Reese Aug 30 '23 at 16:16

0 Answers0