Skip to content

Redux の getState() で状態が取得できない問題と解決策

問題の概要

Redux において createStore() が非推奨となり、代わりに @reduxjs/toolkitconfigureStore() の使用が推奨されています。この変更に伴い、アクション内で getState() を使用して状態を取得しようとすると、以下のような問題が発生することがあります:

javascript
export const addProduct = product => async (dispatch, getState) => {
  try {
    const { userLogin: { userInfo } } = getState() // undefined が返される
    // ...
  } catch (error) {
    // エラーハンドリング
  }
}

getState() から userInfoundefined として返され、認証トークンの取得に失敗します。しかし、getState() を削除するとアクションは正常に動作します。

根本原因

この問題は createStore() の非推奨化自体が直接の原因ではありません。Redux メンテナーによると、createStore の非推奨表示はエディター上の視覚的な指標であり、ランタイムの警告ではないと説明されています。

実際の問題は、ストアの設定方法とミドルウェアの設定に関連しています。

解決策

1. Redux Toolkit の正しい設定方法

configureStore を使用する際は、ミドルウェアの設定を適切に行う必要があります:

javascript
import { configureStore } from '@reduxjs/toolkit'
import thunk from 'redux-thunk'
// 各Reducerのインポート

const reducer = {
  userLogin: userLoginReducer,
  // その他のReducer
}

const userInfoFromStorage = localStorage.getItem('userInfo')
  ? JSON.parse(localStorage.getItem('userInfo'))
  : null

const preloadedState = {
  userLogin: { userInfo: userInfoFromStorage },
}

const store = configureStore({
  reducer,
  preloadedState, // 正しいスペル: preloadedState (not preLoadedState)
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(thunk),
})

export default store

注意

preLoadedState ではなく preloadedState(Lが小文字)が正しいプロパティ名です。このスペルミスが状態の初期化に影響を与える可能性があります。

2. モダンな Redux への移行

Redux Toolkit を使用したより現代的なアプローチ:

javascript
// store.js
import { configureStore } from '@reduxjs/toolkit'
import { userLoginReducer } from './reducers/userReducers'

export const makeStore = () => {
  return configureStore({
    reducer: {
      userLogin: userLoginReducer,
      // その他のReducer
    },
    preloadedState: {
      userLogin: { 
        userInfo: localStorage.getItem('userInfo') 
          ? JSON.parse(localStorage.getItem('userInfo'))
          : null
      }
    },
    // middlewareは自動的に設定される(thunkを含む)
  })
}

3. createStore のレガシーな使用法(非推奨)

どうしても従来の createStore を使用する必要がある場合:

javascript
import { legacy_createStore as createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'

const store = createStore(
  reducer,
  preloadedState,
  composeWithDevTools(applyMiddleware(thunk))
)

非推奨

この方法はレガシーなアプローチであり、新規プロジェクトでは推奨されません。Redux Toolkit を使用することが公式推奨です。

ベストプラクティス

モダンなアクションの書き方

Redux Toolkit では createAsyncThunk を使用することで、より簡潔に非同期アクションを記述できます:

javascript
import { createAsyncThunk } from '@reduxjs/toolkit'

export const addProduct = createAsyncThunk(
  'products/add',
  async (product, { getState }) => {
    const { userLogin: { userInfo } } = getState()
    const config = {
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${userInfo.token}`,
      },
    }
    
    const response = await axios.post('/product', product, config)
    return response.data
  }
)

ストア設定の完全な例

javascript
import { configureStore } from '@reduxjs/toolkit'
import userReducer from './userSlice'
import productReducer from './productSlice'

const store = configureStore({
  reducer: {
    user: userReducer,
    product: productReducer,
  },
  preloadedState: {
    user: {
      userInfo: localStorage.getItem('userInfo')
        ? JSON.parse(localStorage.getItem('userInfo'))
        : null
    }
  },
  devTools: process.env.NODE_ENV !== 'production',
})

export default store

結論

getState()undefined を返す問題は、主に以下の原因が考えられます:

  1. ストア設定の不備preloadedState のスペルミスや不適切な設定
  2. ミドルウェア設定の問題:thunk ミドルウェアが正しく設定されていない
  3. 状態構造の不一致getState() で想定している状態構造と実際の構造が異なる

Redux Toolkit の configureStore を使用し、公式ドキュメントに従った適切な設定を行うことで、これらの問題を解決できます。新しいプロジェクトでは、常に Redux Toolkit を採用し、モダンな Redux パターンを実践することをお勧めします。