skip to Main Content

I am making a chat app with React 18 and Firebase 9.

The chat route – which is the root app – is supposed to be accessible only after authentication.

In App.js I have:

import { useContext } from 'react';
import {
  createBrowserRouter,
  createRoutesFromElements,
  Route,
  RouterProvider,
  Navigate
} from 'react-router-dom';
import RootLayout from './layouts/RootLayout';
import AuthLayout from './layouts/AuthLayout';
import Chat from './pages/Chat';
import Register from './pages/Register';
import Login from './pages/Login';
import { AuthContext } from './contexts/AuthContext';

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/" element={<RootLayout />}>
      <Route index
        element={
          <ProtectedRoute>
            <Chat />
          </ProtectedRoute>
        } />

      <Route path="auth" element={<AuthLayout />}>
        <Route path="register" element={<Register />} />
        <Route path="login" element={<Login />} />
      </Route>
    </Route>
  )
);

function App() {
  const { currentUser } = useContext(AuthContext);

  const ProtectedRoute = ({ children }) => {
    if (!currentUser) {
      return <Navigate to="/auth/login" />;
    }
    return children
  };

  return (
    <RouterProvider router={ router } ProtectedRoute={ ProtectedRoute } />
  );
}

export default App;

In srccontextsAuthContext.js I have:

import { createContext, useEffect, useState } from "react";
import { auth } from "../firebaseConfig";
import { onAuthStateChanged } from "firebase/auth";

export const AuthContext = createContext();
export const AuthContextProvider = ({ children }) => {
  const [currentUser, setCurrentUser] = useState({});

  useEffect(() => {
    const unSubscribe = onAuthStateChanged(auth, (user) => {
      setCurrentUser(user);
      console.log(user);
    });

    return () => {
      unSubscribe();
    };
  }, []);

  return (
    <AuthContext.Provider value={{ currentUser }}>
      {children}
    </AuthContext.Provider>
  );
};

I use the AuthContextProvider in index.js:

import { AuthContextProvider } from './contexts/AuthContext';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <AuthContextProvider>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </AuthContextProvider>
);
reportWebVitals();

The Chrome console throws this error from App.js:

Uncaught ReferenceError: ProtectedRoute is not defined

What am I doing wrong?

UPDATE

This version of App.js throws a React Hook "useContext" cannot be called at the top level error:

const { currentUser } = useContext(AuthContext);

const ProtectedRoute = ({ children, currentUser }) => {
  if (!currentUser) {
    return <Navigate to="/auth/login" />;
  }
  return children
};

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/" element={<RootLayout />}>
      <Route index
        element={
          <ProtectedRoute currentUser={ currentUser }>
            <Chat />
          </ProtectedRoute>
        } />

      <Route path="auth" element={<AuthLayout />}>
        <Route path="register" element={<Register />} />
        <Route path="login" element={<Login />} />
      </Route>
    </Route>
  )
);

export default function App() {
  return (
    <RouterProvider router={router} />
  );
}

2

Answers


  1. For RouterProvider from react-router-dom you don’t have ProtectedRoute prop.

    Login or Signup to reply.
  2. Let’s address the issues step by step:

    Issue 1: Uncaught ReferenceError: ProtectedRoute is not defined

    The error you’re getting is due to the fact that you’re trying to use ProtectedRoute inside the router definition before it is actually defined. JavaScript functions get hoisted, but the const and let variable declarations do not.

    To solve this, we can refactor the App component structure to define ProtectedRoute before using it.

    Issue 2: React Hook "useContext" cannot be called at the top level

    Hooks can only be called inside the body of a function component. Your update tried to call useContext outside of any component, which is why React throws an error.

    Here’s a refactored version of your `App.js:

    import { useContext } from 'react';
    import {
      createBrowserRouter,
      createRoutesFromElements,
      Route,
      RouterProvider,
      Navigate
    } from 'react-router-dom';
    import RootLayout from './layouts/RootLayout';
    import AuthLayout from './layouts/AuthLayout';
    import Chat from './pages/Chat';
    import Register from './pages/Register';
    import Login from './pages/Login';
    import { AuthContext } from './contexts/AuthContext';
    
    function App() {
      const { currentUser } = useContext(AuthContext);
    
      const ProtectedRoute = ({ children }) => {
        if (!currentUser) {
          return <Navigate to="/auth/login" />;
        }
        return children;
      };
    
      const router = createBrowserRouter(
        createRoutesFromElements(
          <Route path="/" element={<RootLayout />}>
            <Route index
              element={
                <ProtectedRoute>
                  <Chat />
                </ProtectedRoute>
              } />
            <Route path="auth" element={<AuthLayout />}>
              <Route path="register" element={<Register />} />
              <Route path="login" element={<Login />} />
            </Route>
          </Route>
        )
      );
    
      return <RouterProvider router={ router } />;
    }
    
    export default App;
    

    By placing the ProtectedRoute component and the useContext hook inside the App component, you’ll ensure hooks are used in the correct context and variables are accessed in the proper sequence.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search