Search code examples
reactjsauthenticationreact-router-dom

react-router-dom Conditional Routing on Submit?


I'm learning react and trying to create a login page, I had the whole thing built and working using conditional rendering. But I decided I wanted to be able to make use of the browser routing (going back and forth, and the benefits of actually having paths. i.e. /Home always takes you to /Home)

So I decided to use react-router and I've tried a myriad of different options in the react router library but I can't for the life of me seem to get it to work and I am not even sure which direction I'm supposed to take because react router is a mix and mash of different versions and flows.

Here is what I currently have (V6), which is the closest I have come to something that works: (note: some things may be left over from other things I have tried)

Server just returns 'PASSSED' or 'FAILED', I know that side of things is working.

//index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import reportWebVitals from './reportWebVitals';
import {
  RouterProvider,
} from "react-router-dom";
import { router } from "./App";



const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <RouterProvider router={router} />
);

reportWebVitals();
//App.js
import React, { useState } from "react";
import './App.css';
import {Login, auth} from './Pages/Login';
import Home from './Pages/Home';
import {
  createBrowserRouter,
  redirect,
  useNavigate,
} from "react-router-dom";

export const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
  },
  {
    path: "Home",
    element: <Home/>,
    loader: loader
  },
],);

function loader() {
  if (!auth)
  {
    return (
      redirect('/')
    );
  }
}

function App() {

  const [authed, setAuthed] = useState();
  const navigate = useNavigate();

  function callback(res) {
    if (res == 'FAILED')
    {
      alert('FAILED');
      return (
        redirect('/')
      );
    } else if (res == 'PASSED') {
      alert('Passed');
      setAuthed(true);
      auth = true;
      return (
        redirect('/Home')
      );
    }
  }
  
  return(
    <div className='App'>
      <h1>VDSL Control Panel</h1>
      <hr></hr>
      <header>
        <Login handleCallback={callback}/>
      </header>
    </div>
  )
} 
//Login.js
import axios from 'axios';

export var auth = false;

export function Login(props) {

  function HandleSubmit(event) {

    //prevent browser from reloading the page
    event.preventDefault();
  
    //read the form data and convert to readable JSON?
    const form = event.target;
    const formData = new FormData(form);
    const formJson = Object.fromEntries(formData.entries());
  
    //Post data to database and Authenticate
    axios.post('http://10.10.47.254:3001/db/login', formJson)
    .then((response) => props.handleCallback(response.data))
    .catch((error) => console.log(error));
  }

  return (
    <div>
      <header>
          <form onSubmit={HandleSubmit} >
            <label>
              Username: <input name='userInput'/>
            </label>
            <br></br>
            <label>
              Password: <input name='passwordInput'/>
            </label>
            <br></br>
            <input type='submit' value='Login' className='Button'/>
          </form>
        </header>
    </div>
  );
}

With this I managed to get redirect to work, but only in the loader function. So when I submit my login it does not redirect. But if I try to access /Home directly it redirects me to the login (via App component)

I have tried:

useNavigate(I just could not for the life of me get to work, at times it would make my screen go white and I could hear my cpu load increase as my fans just went way louder, and nothing would load until I restarted the react app.)

redirect(I think only works in loader),

loader(I wasn't sure how to connect the loader to my submit button.)

I started using the older style of react router with BrowserRouter, Routes, Route and such to no avail as well. Well, I got the routes themselves working, I just couldnt figure out how to handle my auth.

I just want it to redirect me when I submit the form, it doesn't matter which version or method I have to use. I'm not even sure what I'm supposed to be using in this case.

Thanks in advance for taking the time to look this over and help me out.

EDIT: Drew Reese:

  function callback(res) {

    useEffect(() => {
      if (res == 'FAILED')
      {
        navigate('/');

      } else if (res == 'PASSED') {
        alert('Passed');
        navigate('/Home');
      }
    }, [res])
  }

returns error that I cannot use hook in non react component. I changed "callback" to be "Callback" and it doesn't error but it just doesn't trigger either of my alert messages, so the callback stopped working once I made it a react component with a capital "C"? I did also change it to "Callback" on the Login element call in app.js

  function callback(res) {
    alert(res);
    if (res == 'FAILED')
    {
      alert('Failed');
      navigate('/');

    } else if (res == 'PASSED') {
      alert('passed');
      navigate('/Home');
    }
  }

Just does not change url or page at all but I do get the 'passed' alert.


Solution

  • navigate should just be called directly from in the callback handler. Don't use window.alert as this is thread blocking, so the UI will be "paused" until the alert is cleared.

    import { createBrowserRouter, useNavigate } from "react-router-dom";
    
    export const router = createBrowserRouter([
      {
        path: "/",
        element: <App />,
      },
      {
        path: "/home",
        element: <Home />,
      },
    ],);
    
    function App() {
      const [authed, setAuthed] = useState();
    
      const navigate = useNavigate();
    
      function callback(res) {
        if (res == 'FAILED') {
          // This might not be necessary since already on path "/" to authenticate
          navigate('/');
        } else if (res == 'PASSED') {
          setAuthed(true);
          navigate('/home');
        }
        // What to do if res is neither "PASSED" nor "FAILED"?
      }
      
      return (
        <div className='App'>
          <h1>VDSL Control Panel</h1>
          <hr />
          <header>
            <Login handleCallback={callback} />
          </header>
        </div>
      );
    }