vitejs / vite-plugin-react

The all-in-one Vite plugin for React projects.
MIT License
546 stars 102 forks source link

Conditional React lazy routes appears in build #337

Closed Nik96i closed 2 weeks ago

Nik96i commented 2 weeks ago

Describe the bug

Hi. I have some react-router lazy routes and it works fine. now I want some of my routes not to be built based on some conditions. My app has two different modes and every mode has different routes. In a simple example for Page A, I don't want to be in build time. Here is my App.tsx codes.

import './App.css'
import {createBrowserRouter, RouteObject, RouterProvider} from "react-router-dom";
import { lazy} from "react";
import {IS_DEV} from "./constants.ts";

const HomePage = lazy(() => import("./pages/Home"));
const PageA = lazy(() => import("./pages/PageA.tsx"));
const PageB = lazy(() => import("./pages/PageB"));
const PageC = lazy(() => import("./pages/PageC"));

const routes: RouteObject[] = [
    {
        path: "/",
        element: <HomePage />
    },
    ...( IS_DEV ? [
        {
            path: "/page-a",
            element: <PageA />
        }
    ] : [] ),

    {
        path: "/page-b",
        element: <PageB />
    },
    {
        path: "/page-c",
        element: <PageC />
    },
]

const appRouter = createBrowserRouter(routes);

function App() {

  return (
      <div>
          <div>Header</div>
          <br />

          <RouterProvider router={appRouter} />
      </div>
  )
}

export default App

When I build this. PageA is in the final build files: Screenshot 2024-06-13 170731

Here are the codes in the main JS file (index-DbQkpaEw.js):

const ey = _.lazy(() => Fo(() => import("./Home-be3Nhm7b.js"), []));
_.lazy(() => Fo(() => import("./PageA-DJ6zsm_b.js"), []));
const ty = _.lazy(() => Fo(() => import("./PageB-C-BBvG8K.js"), [])),
    ny = _.lazy(() => Fo(() => import("./PageC-Q-ORDu5Q.js"), [])),
    ry = [{
        path: "/",
        element: pt.jsx(ey, {})
    }, {
        path: "/page-b",
        element: pt.jsx(ty, {})
    }, {
        path: "/page-c",
        element: pt.jsx(ny, {})
    }],
    ly = Bv(ry);

It seems PageA doesn't have any variables or something. It's also not used across the app. Why does it exist in the final build and the main JS file?

To fix this issue I have to load PageA like this:

const PageA = IS_DEV ? lazy(() => import("./pages/PageA.tsx")) : undefined;

But it gives a TypeScript error when I want to use it:

TS2604: JSX element type PageA does not have any construct or call signatures.

What is the right way to make lazy imports conditional and not to build them at all?

Reproduction

https://stackblitz.com/edit/vitejs-vite-pqgvpe?file=src%2Fmain.tsx

Steps to reproduce

npm install and npm run build

System Info

System:
    OS: Windows 11 10.0.22631
    CPU: (16) x64 12th Gen Intel(R) Core(TM) i7-12650H
    Memory: 15.88 GB / 31.63 GB
  Binaries:
    Node: 20.14.0 - C:\Program Files\nodejs\node.EXE
    Yarn: 4.3.0 - C:\Program Files\nodejs\yarn.CMD
    npm: 10.8.1 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Chromium (125.0.2535.92)
    Internet Explorer: 11.0.22621.3527

Used Package Manager

yarn

Logs

No response

Validations

ArnaudBarre commented 2 weeks ago

I think this happen because the bundler cannot detect that's it's safe to remove the call to React.lazy. Maybe this call as a side effect and removing it will break the build. I pretty sure lazy is sideEffect free, maybe an annotation could be added to the React build to help on this pattern, this is to be discussed with the React team.

I think the second option is fine, and you will probably avoid the TS issue with this:

const HomePage = lazy(() => import("./pages/Home"));
const PageA = IS_DEV ? lazy(() => import("./pages/PageA.tsx")) : undefined;

const routes: RouteObject[] = [
    {
        path: "/",
        element: <HomePage />
    },
    ...( PageA ? [
        {
            path: "/page-a",
            element: <PageA />
        }
    ] : [] ),
]

(I just found out that the new TS config is broken in stackblitz, so I'm not 100% sure)

Nik96i commented 2 weeks ago

OK. Thank you sir. I use your solution and it works fine.

ArnaudBarre commented 2 weeks ago

Nice!