vite: the vite's HMR does not work when I use React.lazy() API for lazyload

Describe the bug

when I use React.lazy(),like this:

// router/index.ts
import React from 'react';

const Home = React.lazy(() => import('../views/Home'));
const About = React.lazy(() => import('../views/About'));

const routes = [
  {
    path: '/',
    exact: true,
    component: Home
  },
  {
    path: '/about',
    exact: true,
    component: About
  },
  {
    path: '/login',
    exact: true,
    component: React.lazy(() => import('../views/login/login'))
  },
  {
    path: '/form-page',
    exact: true,
    component: React.lazy(() => import('../views/form-test/form-page'))
  },
  {
    path: '/props-up',
    exact: true,
    component: React.lazy(() => import('../views/form-test/props-up'))
  }
];

export default routes;

supplement!I use react-router-config manage my routes information,like this:

// App.tsx
import { renderRoutes } from 'react-router-config';
import routes from '@/router';

// other code

render(): JSX.Element {
  return (
    <Router>
      <div className={styles.container}>
        <div className={styles['title-wraper']}>
          <Link to="/">
            <span className={styles['link-title']}>Home</span>
          </Link>
          <span className={styles['link-line']}> | </span>
          <Link to="/about">
            <span className={styles['link-title']}>About</span>
          </Link>
        </div>
        <Switch>
          <React.Suspense fallback={<Loading></Loading>}>
            {renderRoutes(routes)}
          </React.Suspense>
        </Switch>
      </div>
    </Router>
  );
}

when I modify some code in Home.tsx or About.tsx etc,the HMR was not effected.I need refresh the broswer can see my modification.

if I dont use React.lazy() API,the HMR is normal!

Reproduction

I see vite HMR can work,but it nonitor a wrong file. image

image

I modify About.tsx,but vite HMR feed back router/index.ts.

System Info

- OS: win 10
- browser:Chrome 91
- react(react-dom): 17.0.2
- react-router-dom: 5.2.0
- react-router-config: 5.1.1
- vite: 2.4.2

vite.config.ts

import path from 'path';
import { defineConfig } from 'vite';
import reactRefresh from '@vitejs/plugin-react-refresh';
import eslintPlugin from 'vite-plugin-eslint';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [reactRefresh(), eslintPlugin()],
  resolve: {
    alias: { '@': path.join(__dirname, 'src') }
  },
  css: {
    modules: {
      generateScopedName: '[name]__[local]--[hash:base64:5]'
    }
  }
});


### Used Package Manager

npm

### Logs

```shell
no error!

Validations

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 4
  • Comments: 22 (4 by maintainers)

Most upvoted comments

damn. It looks like if you give function component a name, then both c-r-a and vite example works.

     // Anonymous direct exports like export default function() {}
    // are currently ignored.

reference

import React from 'react'
const Foo = () => ( // works
  <div>foo</div>
)
export default Foo
import React from 'react'
export default () => ( // doesn't works
  <div>foo</div>
)

So, It’s definitely not a bug of vite, but an intentional behavior of react facebook/react#21181.

I’m using @loadable/component and Function components its worked fine for me.

@guibwl After i used @loadable/component, it works fine。Thank

Same issue, My App is like below code:

const A = lazy(() => import('./pages/A'));
const B = lazy(() => import('./pages/B'));

function App() {
  return (
    <HashRouter>
      <Suspense fallback={<div>loading</div>}>
        <Routes>
          <Route path="/a" element={<A />} />
          <Route path="/b" element={<B />} />
        </Routes>
      </Suspense>
    </HashRouter>
  );
}

After use React.lazy, change in Component A (or B) cannot hot reload, but need switch routing.

I’m encountering this issue and can’t find any obvious places where i’m not defining a component as a const before export defaulting it.

Any additional input on how people got over this would be appreciated!

I see… Well, my setup is a bit more complex with routing, custom providers and hooks. In a nutshell, when HMR is triggered for a component in the lazy loaded path, a hook fails to get a parameter from the URL. It just returns a default value I’ve set without actually checking the URL again.

If I find the time, I’ll try making a small reproduction.

import React from 'react'
const Foo = () => ( // works
  <div>foo</div>
)
export default Foo
import React from 'react'
export default () => ( // doesn't works
  <div>foo</div>
)

So, It’s definitely not a bug of vite, but an intentional behavior of react facebook/react#21181.

I think using Foo works because component names have to start with a capital letter: https://reactjs.org/docs/components-and-props.html

Note: Always start component names with a capital letter. React treats components starting with lowercase letters as DOM tags. For example, <div /> represents an HTML div tag, but <Welcome /> represents a component and requires Welcome to be in scope.

https://github.com/iheyunfei/vite-bug-report-4298 I create a reproduction repo. I both using create-react-app and vite to reproduce the problem successfully. So, I guess this is the problem of upstream react-refresh, not vite.

And the problem only happens on function component, Class Component seems to work.

I’m using @loadable/component and Function components its worked fine for me.