Typescript Page Routing does not render subpage when clicked (React Router v6)

31 views Asked by At

I have an application on the landing page (App.tsx). After inputting your email and clicking a "Let's get started" button, it is supposed to propagate the email parameter to the Get Started page and render the GetStarted component. Right now, clicking the "Let's get started" button does not render anything new/route to the new page. I've logged comments throughout my flow, but I noticed the only comment that gets logged is in App.tsx. This is my logic:

When clicking the button "Let's get started" in App.tsx, this calls the handleCreateProfileClick(). Inside this function, I call the <GetStartedPage /> component, with the email the user types in as the parameter. I have an AppRouter.tsx file that handles all routing for the application (including GetStartedPage ofc). Shouldn't calling the GetStartedPage on a button click render the page, since AppRouter links the diff. slugs to each page? Provided code below and omitted unimportant logic for conciseness.

This is my App.tsx, which has the button:

import React, { useState, useEffect, useRef, ReactNode } from 'react';
import signinIcon from 'icons/signin_icon.png';
import './App.css';
import GetStartedPage from './GetStartedPage';
import SignInPage from './SignInPage';

const App = () : ReactNode => {
  const [showSidePanel, setShowSidePanel] = useState(false);
  const [email, setEmail] = useState('');

  const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setEmail(event.target.value);
  };

  const  handleCreateProfileClick = () => {
    const emailInput = document.getElementById('email-input') as HTMLInputElement;
    const email = emailInput.value;

    if (!email) {
      alert('Please enter your email');
      return;
    }
    console.log("About to navigate from handleCreateProfile in App.tsx");
    return <GetStartedPage inputEmail={email} />
  };

  const handleSignInClick = () => {
    return <SignInPage />
  };

  const handleResize = () => {
    if (window.innerWidth > 600) {
      setShowSidePanel(false); // Hide the side panel if the screen size is greater than 600px
    }
  };

  useEffect(() => {
    window.addEventListener('resize', handleResize); // Add event listener for window resize
    return () => {
      window.removeEventListener('resize', handleResize); // Remove event listener on component unmount
    };
  }, []); // Empty dependency array ensures that the effect runs only once on component mount

  
  return (
      <div className="App">
      <div id="page-container">
      <div className="menu">
        <div className="menu-item sign-up" onClick={handleSignInClick}>
          <img src={signinIcon} alt="Sign in" className="signin"/>
          <span className="sign-up-text">Sign in</span>
        </div>
      </div>
      <div className="container">
        <div className="left-content">
      <div className="email-form">
        <input
          type="email"
          placeholder="Email address"
          onChange={handleEmailChange}
          value={email}
          id="email-input"
          required
        />
        <button className="lets-go-button" onClick={handleCreateProfileClick}>
          Let's get started
        </button>
      </div>
      <div className="sign-in">
        Already joined? <u><a href="/signin" onClick={handleSignInClick}>Sign in</a></u>.
      </div>
    </div>
    </div>
    </div>
    </div>
    );
};

export default App;

This is my AppRouter.tsx:

import React, { useState } from 'react';
import { BrowserRouter as Router, Route, Routes, useNavigate, useLocation } from 'react-router-dom';
import App from './App';
import GetStartedPage from './GetStartedPage';

interface AppRouterProps {
  email: string;
}

const AppRouter: React.FC<AppRouterProps> = ({ email }) => {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<App />} />
        <Route path="/create-profile" element={<GetStartedPage inputEmail={email} />} />
        <Route path="/sign-in" element={<SignInPage />} />
      </Routes>
    </Router>
  );
};

export default AppRouter;

This is my GetStarted.tsx page:

import React, { useState, useEffect } from 'react';
import axios from 'axios';
interface GetStartedPageProps {
  inputEmail: string;
}

const GetStartedPage: React.FC<GetStartedPageProps> = (props) => {
  // More details here (omitted)
  console.log("Currently in Create profile page");
  // Updating the email state when the inputEmail prop changes, which we get from App.tsx
  useEffect(() => {
    setEmail(props.inputEmail);
  }, [props.inputEmail]);

    // More details here (omitted)
   
    try {
      await axios.post('/createProfile', userData);
      alert('Profile created successfully');
    } catch (error) {
      console.error('Error creating profile:', error);
      alert('Error creating profile');
    }
  };

  return (
    // More details here (omitted)
  );
};

export default GetStartedPage;

This is my index.tsx:

import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import store from './redux/store';
import App from '../../frontend/src/App';

const container = document.getElementById('root');
if (container) {
  const root = createRoot(container);

  root.render(
    <Provider store={store}>
      {/* @ts-ignore */}
      <App>
      </App>
    </Provider>
  );
} else {
  console.error('Container element not found!');
}
1

There are 1 answers

2
Drew Reese On BEST ANSWER

Returning JSX from event handlers isn't how React components render additional UI. From the code and your description it seems like you should navigate to the "/create-profile" and "/sign-in" routes to render the GetStartedPage and SignInPage components.

For this you should import and use the useNavigate hook to access the navigate function and issue imperative navigation actions to these specific routes. For the raw anchor tag you should import and render the Link component.

Example:

import { Link, useNavigate } from 'react-router-dom';

...

const App = () : ReactNode => {
  const navigate = useNavigate();

  const [showSidePanel, setShowSidePanel] = useState(false);
  const [email, setEmail] = useState('');

  const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setEmail(event.target.value);
  };

  const  handleCreateProfileClick = () => {
    const emailInput = document.getElementById('email-input') as HTMLInputElement;
    const email = emailInput.value;

    if (!email) {
      alert('Please enter your email');
      return;
    }
    
    return navigate("/create-profile"); // <-- navigate
  };

  const handleSignInClick = () => {
    return navigate("/sign-in"); // <-- navigate
  };

  ...

  
  return (
    <div className="App">
      <div id="page-container">
        <div className="menu">
          <div className="menu-item sign-up" onClick={handleSignInClick}>
            <img src={signinIcon} alt="Sign in" className="signin" />
            <span className="sign-up-text">Sign in</span>
          </div>
        </div>
        <div className="container">
          <div className="left-content">
            <div className="email-form">
              <input
                type="email"
                placeholder="Email address"
                onChange={handleEmailChange}
                value={email}
                id="email-input"
                required
              />
              <button
                type="button"
                className="lets-go-button"
                onClick={handleCreateProfileClick}
              >
                Let's get started
              </button>
            </div>
            <div className="sign-in">
              Already joined? <Link to="/signin"><u>Sign in</u></a>.
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default App;