0

I'm making a Single Page Application in React and i'm using the "lazy" function and the "Suspense" Component to import components only when is needed. But i have an strange error that i don't understand that says:

Error: Uncaught TypeError: can't convert item to string

My code in App.jsx:

import "./App.css";
import { lazy, Suspense } from "react";
import { Route } from "./Route.jsx";
import { Router } from "./Router.jsx";

const Home = lazy(() => import("./pages/Home.jsx"));
const About = lazy(() => import("./pages/About.jsx"));
const Search = lazy(() => import("./pages/Search.jsx"));

function App() {
  return (
    <main>
      <Suspense fallback={null}>
        <Router defaultComponent={() => <h1>404</h1>}>
          <Route path="/" Component={Home}></Route>
          <Route path="/about" Component={About}></Route>
          <Route path="/search/:query" Component={Search}></Route>
        </Router>
      </Suspense>
    </main>
  );
}

export default App;

I tried by asking ChatGPT and posting this problem in a discord server but they ignored me :(

2
  • Why? The components are already in your bundle, what is there to load later? Commented May 26, 2024 at 20:58
  • This depends on how your <Route component actually works. If it's a wrapper around react router you could try using element={<Home />} instead which will trigger an attempt to create the element and hopefully propagate a loading state Commented May 26, 2024 at 21:18

4 Answers 4

0

when you use lazy you have to use it like this, instead of Component attribute inside the Route you have to use element and the value of element should me like this <lazyVariableName/>

import "./App.css";
import { lazy, Suspense } from "react";
import { Route } from "./Route.jsx";
import { Router } from "./Router.jsx";

const Home = lazy(() => import("./pages/Home.jsx"));
const About = lazy(() => import("./pages/About.jsx"));
const Search = lazy(() => import("./pages/Search.jsx"));

function App() {
  return (
    <main>
      <Suspense fallback={null}>
        <Router defaultComponent={() => <h1>404</h1>}>
          <Route path="/" element={<Home/>}></Route>
          <Route path="/about" element={<About/>}></Route>
          <Route path="/search/:query" element={<Search/>}></Route>
        </Router>
      </Suspense>
    </main>
  );
}

export default App;
Sign up to request clarification or add additional context in comments.

Comments

0

While doing it that way does not give any more errors, it still does not take you to the pages in question. I guess it is due to the logic used by the whole program. What this does is to have a component called “Route” that has “path” and “Component” as parameters. The idea is that in the “Router”, it makes a .map of the “Route” children and returns in the variable “Page” the component to use, but as now the parameter “Component” is no longer used in “Route”, but the parameter “element”, it does not work. I have tried changing the “.Component” at the end of the declaration of the variable “Page” by “.element” but that doesn't work either. Do you have any idea why?

App.jsx:

import "./App.css";
import { lazy, Suspense } from "react";
import { Route } from "./Route.jsx";
import { Router } from "./Router.jsx";

const Home = lazy(() => import("./pages/Home.jsx"));
const About = lazy(() => import("./pages/About.jsx"));
const Search = lazy(() => import("./pages/Search.jsx"));

function App() {
  return (
    <main>
      <Suspense fallback={null}>
        <Router defaultComponent={() => <h1>404</h1>}>
          <Route path="/" element={<Home/>}></Route>
          <Route path="/about" element={<About/>}></Route>
          <Route path="/search/:query" element={<Search/>}></Route>
        </Router>
      </Suspense>
    </main>
  );
}

export default App;

Route.jsx:

// eslint-disable-next-line no-unused-vars
export function Route({ path, Component }) {
  return null;
}

Router.jsx:

import { EVENTS } from "./constants.js";
import { useState, useEffect, Children } from "react";
import { match } from "path-to-regexp";

export function Router({
  children,
  routes = [],
  defaultComponent: DefaultComponent = () => <h1>404</h1>,
}) {
  const [currentPath, setCurrentPath] = useState(window.location.pathname);

  useEffect(() => {
    const onLocationChange = () => {
      setCurrentPath(window.location.pathname);
    };

    window.addEventListener(EVENTS.PUSHSTATE, onLocationChange);
    window.addEventListener(EVENTS.POPSTATE, onLocationChange);

    return () => {
      window.removeEventListener(EVENTS.PUSHSTATE, onload);
      window.removeEventListener(EVENTS.POPSTATE, onLocationChange);
    };
  }, []);

  let routeParams = {};

  const routesFromChildren = Children.map(children, ({ props, type }) => {
    const { name } = type;
    const isRoute = name == "Route";

    return isRoute ? props : null;
  });

  let routesToUse = routes.concat(routesFromChildren);

  const Page = routesToUse.find(({ path }) => {
    if (path == currentPath) return true;

    const matcherUrl = match(path, { decode: decodeURIComponent });
    const matched = matcherUrl(currentPath);
    if (!matched) return false;

    routeParams = matched.params;
    return true;
  })?.Component;

  return Page ? (
    <Page routeParams={routeParams} />
  ) : (
    <DefaultComponent routeParams={routeParams} />
  );
}

Comments

0

The imported page must be a default export. This means the home, about and search pages need to have export default function About.

I don't know the reason behind this, but lazy doesn't know which component to renderize without default

Comments

0

I had the same issue, You can use "The imported page must be a default export. This means the home, about and search pages need to have export default function About."

2 Comments

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
This does not really answer the question. If you have a different question, you can ask it by clicking Ask Question. To get notified when this question gets new answers, you can follow this question. Once you have enough reputation, you can also add a bounty to draw more attention to this question. - From Review

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.