MeWrite Docs

Vite: HMR Cannot access before initialization

ViteのHMRで循環依存により変数が初期化前にアクセスされるエラー

概要

ViteのHot Module Replacement(HMR)使用時に、循環依存のあるモジュールでファイルを保存すると「Cannot access ‘xxx’ before initialization」エラーが発生する問題です。通常のページリロードでは動作しますが、HMRによるホットリロード時にのみ発生します。

エラーメッセージ

Uncaught ReferenceError: Cannot access 'store' before initialization
    at Module.store (store.js:10:1)
    at eval (Component.jsx:5:1)

または:

ReferenceError: Cannot access 'AppContext' before initialization

原因

  1. 循環依存: モジュールAがモジュールBをインポートし、モジュールBがモジュールAをインポートしている
  2. HMRのモジュール再評価順序: HMR時のモジュール再評価順序が通常の初期化順序と異なる
  3. Temporal Dead Zone(TDZ): constletで宣言された変数が初期化前にアクセスされる
  4. Reduxのconnect()パターン: コンポーネントとストアの間で循環依存が発生しやすい

解決策

1. 循環依存を解消(推奨)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Before: 循環依存
// store.js
import { rootReducer } from './reducers';
export const store = createStore(rootReducer);

// reducers.js
import { store } from './store'; // 循環!

// After: 依存関係を整理
// store.js
import { rootReducer } from './reducers';
export const store = createStore(rootReducer);

// reducers.js - storeを直接インポートしない
export const rootReducer = combineReducers({ ... });

2. 遅延インポートを使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Before
import { store } from './store';

function doSomething() {
  store.dispatch(action);
}

// After
function doSomething() {
  const { store } = await import('./store');
  store.dispatch(action);
}

3. 依存性注入パターン

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Before: 直接インポート
import { api } from './api';

export function fetchData() {
  return api.get('/data');
}

// After: 依存性を引数で受け取る
export function fetchData(api) {
  return api.get('/data');
}

4. バレルファイルの見直し

1
2
3
4
5
6
7
8
// Before: index.jsで全てをre-export(循環の原因に)
// components/index.js
export * from './Header';
export * from './Footer';
export * from './Sidebar';

// After: 必要なものだけ直接インポート
import { Header } from './components/Header';

5. HMRを特定モジュールで無効化

1
2
3
4
// 循環依存のあるモジュールでHMRを無効化
if (import.meta.hot) {
  import.meta.hot.invalidate();
}

6. vite.config.jsでoptimizeDepsを調整

1
2
3
4
5
6
// vite.config.js
export default defineConfig({
  optimizeDeps: {
    exclude: ['problematic-module'],
  },
});

7. Reduxの場合はフック経由でアクセス

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Before: connect()で循環依存
import { connect } from 'react-redux';
import { store } from './store';

// After: useSelector/useDispatchを使用
import { useSelector, useDispatch } from 'react-redux';

function Component() {
  const data = useSelector(state => state.data);
  const dispatch = useDispatch();
}

デバッグ方法

循環依存の検出

1
2
# madgeで循環依存を検出
npx madge --circular src/
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// vite.config.js でプラグインを使用
import circularDependency from 'vite-plugin-circular-dependency';

export default defineConfig({
  plugins: [
    circularDependency({
      failOnError: true,
    }),
  ],
});

よくある間違い

  • 全てのインポートをバレルファイル(index.js)経由にする
  • Reduxのストアをコンポーネントから直接インポートする
  • 循環依存を放置してページリロードで回避し続ける

関連エラー

参考リンク

JavaScript の他のエラー

最終更新: 2025-12-13