vite icon indicating copy to clipboard operation
vite copied to clipboard

HMR error: Cannot access '...' before initialization

Open edikdeisling opened this issue 4 years ago • 55 comments

Describe the bug

The error happens when I try to edit component, that

  1. Wrap in connect function (redux)
  2. Is inside dependency loop
  3. There is another component inside dependency loop

Seems weird, but it's not so rare case when project uses routers like UiRouter or Universal router

I expect that component will be updated with HMR without errors or may be reload the page but not to throw an error

Reproduction

Repo: https://github.com/xSorc/test-vite-fast-refresh-loop-dependency

To reproduce this error you need to open the project and try to edit Component.tsx. You will see an error image

System Info

Output of npx envinfo --system --npmPackages vite,@vitejs/plugin-vue --binaries --browsers:

  System:
    OS: Windows 10 10.0.19042
    CPU: (6) x64 Intel(R) Core(TM) i5-8600K CPU @ 3.60GHz
    Memory: 8.19 GB / 15.94 GB
  Binaries:
    Node: 14.15.3 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.10 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 6.14.9 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Edge: Spartan (44.19041.423.0), Chromium (89.0.774.77)
    Internet Explorer: 11.0.19041.1
  npmPackages:
    vite: ^2.1.5 => 2.1.5

Used package manager:

Logs



Before submitting the issue, please make sure you do the following

  • [x] Read the Contributing Guidelines.
  • [x] Read the docs.
  • [x] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
  • [x] Provide a description in this issue that describes the bug.
  • [x] Make sure this is a Vite issue and not a framework-specific issue. For example, if it's a Vue SFC related bug, it should likely be reported to https://github.com/vuejs/vue-next instead.
  • [x] Check that this is a concrete bug. For Q&A open a GitHub Discussion or join our Discord Chat Server.

edikdeisling avatar Apr 17 '21 19:04 edikdeisling

@xSorc Hello ~ I read your description and repeated the problem, I guess it might be the React Render problem image

ygj6 avatar Apr 21 '21 06:04 ygj6

@ygj6 Hello. Why do you think that it is react problem? I think react only calls render fn. I don't have whole picture, but here some thoughts:

We can't access variable from module, that already depend on us. But this app works - because we access variable not instantly(during module loading), but during render. If we write smth like this - app will fall instantly

import { STATES_CONST } from './states';

console.log(STATES_CONST); // this line break all because we try access STATES_CONST during module loading

class Component extends PureComponent {
    render() {
        return <div>States const: {STATES_CONST}</div>;
    }
}

maybe this issue has to be fixed like this(this is Vite code)?

try {
    const newMod = await import(
    /* @vite-ignore */
    base +
        path.slice(1) +
        `?import&t=${timestamp}${query ? `&${query}` : ''}`);
    moduleMap.set(dep, newMod);
}
catch (e) {
    warnFailedFetch(e, dep);
    location.reload(); // I was added this line
}

P.S. this hmr fails because during loading of dependencies of Component.tsx after hmr there is situation that ComponentOther.tsx loads before router.ts. And router.ts access variable during module loading(this is forbidden) image

maybe it's project problem, but I think Vite should reload the page in this case?

edikdeisling avatar Apr 22 '21 15:04 edikdeisling

I think there is a bug in plugin-react-refresh, but we can not reload on error. Check out the warning message "This could be due to syntax errors or importing non-existent modules.". When the user is typing and there is an error, we want to wait until a successful run to update the page with HMR, or the state will be lost after the full reload while the user is typing (for example if it is using auto save)

patak-dev avatar Apr 23 '21 11:04 patak-dev

@patak-js how about this idea?

We can't update this module because of imports loop, but we can prevent our page to be broken. Now every module call performReactRefresh during loading. We can change it and call react refresh only after successful import. Something like this:

current code here: plugin-react-refresh/index.js

const runtimeCode = `
const exports = {}
${fs.readFileSync(runtimeFilePath, 'utf-8')}
let queue = false;
exports.queueReactRefresh = () => (queue = true); // do not debounce. Only set flag
window.flushReactRefresh = () => { // set window fn to flush updates. We call them *after* successful import 
  if (queue) {
    exports.performReactRefresh();
    queue = false;
  }
}
export default exports

...

    const footer = `
if (import.meta.hot) {
  window.$RefreshReg$ = prevRefreshReg;
  window.$RefreshSig$ = prevRefreshSig;
  ${
    isReasonReact || isRefreshBoundary(result.ast)
      ? `import.meta.hot.accept();`
      : ``
  }
  RefreshRuntime.queueReactRefresh(); // call here queue instead of debounce performReactRefresh
}`

current code here: vite/dist/client/client.js

try {
    const newMod = await import(
    /* @vite-ignore */
    base +
        path.slice(1) +
        `?import&t=${timestamp}${query ? `&${query}` : ''}`);
    window.flushReactRefresh?.(); // flush updates after successful import
    moduleMap.set(dep, newMod);
}
catch (e) {
    warnFailedFetch(e, dep);
}

There is another option how to fix this by one line. We can make something like this:

try {
    const newMod = await import(
    /* @vite-ignore */
    base +
        path.slice(1) +
        `?import&t=${timestamp}${query ? `&${query}` : ''}`);
    moduleMap.set(dep, newMod);
}
catch (e) {
    clearTimeout(window.__vite_plugin_react_timeout); // I added this line
    warnFailedFetch(e, dep);
}

edikdeisling avatar Apr 25 '21 11:04 edikdeisling

I understood that we can't write code connected to react inside Vite client... Needs more complex solution

edikdeisling avatar Apr 26 '21 16:04 edikdeisling

I have the same question, any progress?

flasco avatar May 09 '21 08:05 flasco

I have the same question when i use store and axios in route beforeEach

wellfrog16 avatar Jun 29 '21 15:06 wellfrog16

I have the same question, any progress?

So do i, wait for a solution

wen81643956 avatar Jul 02 '21 10:07 wen81643956

I have the same question, any progress?

pasBone avatar Jul 12 '21 02:07 pasBone

I have the same question, any progress?

Akimotorakiyu avatar Jul 22 '21 18:07 Akimotorakiyu

I have the same question, any progress?

NomadBin avatar Jul 28 '21 10:07 NomadBin

I have the same question, any progress?

etedesco avatar Aug 15 '21 17:08 etedesco

@xSorc were you able to identify the source of this issue? I'm sure there are thousands of Vite apps with react and connected components, so there must be something specific that is causing this issue. I've had to turn off HMR for the app to load properly.

tzusman avatar Aug 25 '21 14:08 tzusman

@tzusman I can't understand the whole picture. All I know that the problem is caused by imports loop. For example you have this structure image

That works during first load. But when I tried to change Component 1, an error Cannot access 'STATES' before initialization happend. The most strange thing is if I change in Component 1 line

function connect(Cmp: ComponentType) {
   return Cmp
}

class Component extends PureComponent {
   render() {
       return <div>States const: {STATES_CONST}</div>;
   }
}

export default connect(Component);

to lines

function connect(Cmp: ComponentType) {
    return Cmp
}

class Component extends PureComponent {
    render() {
        return <div>States const: {STATES_CONST}</div>;
    }
}

const a = connect(Component);
export default a;

the problem is gone. I have no idea how this works

The structure may be simplified to this image so it may be different

My advice is to remove a dependency loop. I've done it in my project. This is the easiest way to handle it right now

edikdeisling avatar Aug 27 '21 07:08 edikdeisling

This also happen to me on development. Build and serve is work well. Screenshot_2021-09-16_17-42-16

Oungseik avatar Sep 16 '21 11:09 Oungseik

Getting same error

matt-erhart avatar Sep 24 '21 18:09 matt-erhart

the same

nawbc avatar Sep 29 '21 15:09 nawbc

Stumbled onto some tips from parceljs that may help with this issue:

  • Avoid class components – Fast Refresh only works with function components (and Hooks).
  • Export only React components – If a file exports a mix of React components and other types of values, its state will be reset whenever it changes. To preserve state, only export React components and move other exports to a different file if possible.
  • Avoid unnamed default exports – Declaring components using a default exported arrow function will cause state to be reset when it is changed. Use a named function, or assign the arrow function to a variable instead.
  • Keep entry components in their own files – Entry components should be in a separate file from the one that calls ReactDOM.render or they will be remounted on every change. For more tips, see the official React Fast Refresh docs. https://reactnative.dev/docs/fast-refresh

I actually was getting a hard refresh way more than I was expecting before following each of those tips, now I'm seeing fast refresh everywhere and haven't seen that error yet.

matt-erhart avatar Oct 01 '21 00:10 matt-erhart

I have the same question, any progress?

Dodd2013 avatar Oct 14 '21 08:10 Dodd2013

@matt-erhart I had the same issue but your last tip really helped me resolve the issue, thank you so much!

What I had:

//main.tsx
const AppProviders: React.FC = (props) => {...}

ReactDOM.render(
  <React.StrictMode>
    <AppProviders>
      <App />
    </AppProviders>
  </React.StrictMode>,
  document.getElementById('root')
);

Changed it to:

// AppProviders.tsx
const AppProviders: React.FC = (props) => {...}

// main.tsx
ReactDOM.render(
  <React.StrictMode>
    <AppProviders>
      <App />
    </AppProviders>
  </React.StrictMode>,
  document.getElementById('root')
);

So I just moved AppProviders to a separate file and everything started working. Thanks again!

jsefiani avatar Oct 17 '21 12:10 jsefiani

For future travelers: If you see the ReferenceError: Cannot access (…) before initialization error then you may have circular dependencies that need to be resolved. Discover them with a tool like Madge: madge --circular <path>

michaeljohansen avatar Nov 18 '21 20:11 michaeljohansen

vs code extension to help find circular dependencies https://marketplace.visualstudio.com/items?itemName=iulian-radu-at.find-unused-exports

source https://twitter.com/alecdotbiz/status/1461729042604998664?t=MJApPe1FxTzI6zMMXXSktQ&s=19

Niputi avatar Nov 19 '21 16:11 Niputi

I have the same question, any progress?

semmy2010 avatar Dec 23 '21 03:12 semmy2010

I have the same question, any progress?

wanghuajin-bello avatar Mar 04 '22 10:03 wanghuajin-bello

I have the same question, any progress?

mokus avatar Mar 30 '22 11:03 mokus

I had this problem too and it was caused by circular dependencies. As others suggested, madge can help you find the files where this is happening, but in my case I had a project with TypeScript and JavaScript files, so I used this command instead:

npx madge --ts-config ./tsconfig.json --warning --circular ./src 

You need to specify the location of your tsconfig.json. Otherwise, the .ts files will be ignored (they will generate a warning instead) and any circular dep in those files will be skipped. The ---warning flag just provides more information (like if you have .json files being used as modules).

damianmr avatar Apr 07 '22 22:04 damianmr

Hello im having the same issue. i have been on it all day and still no answer. i keep getting it no matter what

Screen Shot 2022-04-12 at 1 21 47 PM

ishaiavrahami avatar Apr 12 '22 10:04 ishaiavrahami

@ishaiavrahami as others have mentioned try to run npx madge --circular /src/components/Store.vue to find out the circular dependency.

mehedi-hasan-sunny avatar Apr 12 '22 15:04 mehedi-hasan-sunny

madge don't find any circular dependencies for me and I'm still getting this error

mokus avatar Apr 12 '22 15:04 mokus

How do I fix this issue! Been stuck all this 2 days now

ishaiavrahami avatar Apr 13 '22 05:04 ishaiavrahami