Search code examples
javascriptreactjsreact-routerreact-router-dom

How to route a modal login/register form in React?


I'm working on my diploma project web site. There is a button on the main page which makes a modal window with a login form pops up. Also there is a button on this login form which makes a login form toggle to a register form in the same modal window. I struggled routing login modal window and register modal window.

MainPage.js

import React, { useState } from "react";
import "../App.css";
import Footer from "../Components/Footer";
import Navbar from "../Components/Navbar";
import Button from "../Components/Button";
import ModalLogin from "../Components/ModalLogin";
import RegisterModal from "../Components/RegisterModal";

const MainPage = () => {
  const [isModalOpen, setModalOpen] = useState(false);
  const [isLoginForm, setLoginForm] = useState(true);

  function authorize() {
    setModalOpen(true);
    setLoginForm(true);
  }

  function closeModal() {
    setModalOpen(false);
  }

  function toggleForm() {
    setLoginForm((prev) => !prev);
  }

  return (
    <div className="app-container">
      <div className="main-container">
        <Navbar />
        <div className="intro-section-container">
          <h1>
            Цифровая
            <br />
            образовательная платформа
            <br />
            “Название не придумал”
          </h1>
          <p>
            Откройте дверь в мир музыки с нашим электронным дневником.
            <br />
            Присоединяйтесь прямо сейчас!
          </p>
          <div className="intro-buttons-container">
            <Button color="button-primary" label="Войти" onClick={authorize} />
            <Button color="button-outlined" label="Подключить школу" />
          </div>
        </div>
        {isModalOpen &&
          (isLoginForm ? (
            <ModalLogin onToggleForm={toggleForm} onClose={closeModal} />
          ) : (
            <RegisterModal onToggleForm={toggleForm} onClose={closeModal} />
          ))
        }
      </div>
      <Footer />
    </div>
  );
};

export default MainPage;

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import NoPage from './Pages/NoPage';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<App />} />
        <Route path="*" element={<NoPage />} />
      </Routes>
    </BrowserRouter>
  </React.StrictMode>
);

App.js

import "./App.css";
import MainPage from "./Pages/MainPage";

function App() {
  return (
    <MainPage />
  );
}

export default App;

I've tried different ways routing these forms, but none worked.


Solution

  • I want to route them, so if I open login modal I need my address be like "localhost:3000/login", or "localhost:3000/register" when register modal window opened.

    Import and use the useNavigate hook to issue imperative navigation actions to the appropriate "/login" and "/register" URL paths when the modal UI is toggled. Use a useEffect hook with a dependency on the state variables to effect the navigation actions when the states update.

    import { useNavigate } from 'react-router-dom';
    
    const MainPage = () => {
      const navigate = useNavigate();
    
      const [isModalOpen, setModalOpen] = useState(false);
      const [isLoginForm, setLoginForm] = useState(true);
    
      useEffect(() => {
        if (isModalOpen) {
          // Modal is open, redirect to login or register path
          navigate(isLoginForm ? "/login" : "/register", { replace: true });
        } else {
          // Modal is closed, redirect back to home page path
          navigate("/", { replace: true });
        }
      }, [isModalOpen, isLoginForm]);
    
      function authorize() {
        setModalOpen(true);
        setLoginForm(true);
      }
    
      function closeModal() {
        setModalOpen(false);
      }
    
      function toggleForm() {
        setLoginForm((prev) => !prev);
      }
    
      return (
        ...
      );
    };
    

    You'll need to update the router to render the MainPage, e.g. App on all these routes so the UI remains mounted and rendered.

    Example:

    <BrowserRouter>
      <Routes>
        <Route path="/" element={<App />} />
        <Route path="/login" element={<App />} />
        <Route path="/register" element={<App />} />
    
        <Route path="*" element={<NoPage />} />
      </Routes>
    </BrowserRouter>